Error on duplicate attributes
This commit is contained in:
parent
8e4da7f36b
commit
28589620f6
@ -1,7 +1,7 @@
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use syntax::ast::{self, TokenTree};
|
use syntax::ast::{self, TokenTree};
|
||||||
use syntax::attr;
|
use syntax::attr;
|
||||||
use syntax::codemap::Span;
|
use syntax::codemap::{Span, Spanned, respan};
|
||||||
use syntax::ext::base::ExtCtxt;
|
use syntax::ext::base::ExtCtxt;
|
||||||
use syntax::fold::Folder;
|
use syntax::fold::Folder;
|
||||||
use syntax::parse::parser::{Parser, PathStyle};
|
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 syntax::ptr::P;
|
||||||
|
|
||||||
use aster::AstBuilder;
|
use aster::AstBuilder;
|
||||||
|
use aster::ident::ToIdent;
|
||||||
|
|
||||||
use error::Error;
|
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)]
|
#[derive(Debug)]
|
||||||
pub struct Name {
|
pub struct Name {
|
||||||
ident: ast::Ident,
|
ident: ast::Ident,
|
||||||
@ -22,14 +81,6 @@ pub struct Name {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl 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.
|
/// Return the container name for the container when serializing.
|
||||||
pub fn serialize_name(&self) -> InternedString {
|
pub fn serialize_name(&self) -> InternedString {
|
||||||
match self.serialize_name {
|
match self.serialize_name {
|
||||||
@ -69,55 +120,47 @@ pub struct ContainerAttrs {
|
|||||||
impl ContainerAttrs {
|
impl ContainerAttrs {
|
||||||
/// Extract out the `#[serde(...)]` attributes from an item.
|
/// Extract out the `#[serde(...)]` attributes from an item.
|
||||||
pub fn from_item(cx: &ExtCtxt, item: &ast::Item) -> Result<Self, Error> {
|
pub fn from_item(cx: &ExtCtxt, item: &ast::Item) -> Result<Self, Error> {
|
||||||
let mut container_attrs = ContainerAttrs {
|
let mut ser_name = Attr::none(cx, "rename");
|
||||||
name: Name::new(item.ident),
|
let mut de_name = Attr::none(cx, "rename");
|
||||||
deny_unknown_fields: false,
|
let mut deny_unknown_fields = BoolAttr::none(cx, "deny_unknown_fields");
|
||||||
ser_bound: None,
|
let mut ser_bound = Attr::none(cx, "bound");
|
||||||
de_bound: None,
|
let mut de_bound = Attr::none(cx, "bound");
|
||||||
};
|
|
||||||
|
|
||||||
for meta_items in item.attrs().iter().filter_map(get_serde_meta_items) {
|
for meta_items in item.attrs().iter().filter_map(get_serde_meta_items) {
|
||||||
for meta_item in meta_items {
|
for meta_item in meta_items {
|
||||||
|
let span = meta_item.span;
|
||||||
match meta_item.node {
|
match meta_item.node {
|
||||||
// Parse `#[serde(rename="foo")]`
|
// Parse `#[serde(rename="foo")]`
|
||||||
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"rename" => {
|
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"rename" => {
|
||||||
let s = try!(get_str_from_lit(cx, name, lit));
|
let s = try!(get_str_from_lit(cx, name, lit));
|
||||||
container_attrs.name.serialize_name = Some(s.clone());
|
ser_name.set(span, s.clone());
|
||||||
container_attrs.name.deserialize_name = Some(s);
|
de_name.set(span, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse `#[serde(rename(serialize="foo", deserialize="bar"))]`
|
// Parse `#[serde(rename(serialize="foo", deserialize="bar"))]`
|
||||||
ast::MetaItemKind::List(ref name, ref meta_items) if name == &"rename" => {
|
ast::MetaItemKind::List(ref name, ref meta_items) if name == &"rename" => {
|
||||||
let (ser_name, de_name) = try!(get_renames(cx, meta_items));
|
let (ser, de) = try!(get_renames(cx, meta_items));
|
||||||
if ser_name.is_some() {
|
ser_name.set_opt(ser);
|
||||||
container_attrs.name.serialize_name = ser_name;
|
de_name.set_opt(de);
|
||||||
}
|
|
||||||
if de_name.is_some() {
|
|
||||||
container_attrs.name.deserialize_name = de_name;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse `#[serde(deny_unknown_fields)]`
|
// Parse `#[serde(deny_unknown_fields)]`
|
||||||
ast::MetaItemKind::Word(ref name) if name == &"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")]`
|
// Parse `#[serde(bound="D: Serialize")]`
|
||||||
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"bound" => {
|
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"bound" => {
|
||||||
let where_predicates = try!(parse_lit_into_where(cx, name, lit));
|
let where_predicates = try!(parse_lit_into_where(cx, name, lit));
|
||||||
container_attrs.ser_bound = Some(where_predicates.clone());
|
ser_bound.set(span, where_predicates.clone());
|
||||||
container_attrs.de_bound = Some(where_predicates);
|
de_bound.set(span, where_predicates);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse `#[serde(bound(serialize="D: Serialize", deserialize="D: Deserialize"))]`
|
// Parse `#[serde(bound(serialize="D: Serialize", deserialize="D: Deserialize"))]`
|
||||||
ast::MetaItemKind::List(ref name, ref meta_items) if name == &"bound" => {
|
ast::MetaItemKind::List(ref name, ref meta_items) if name == &"bound" => {
|
||||||
let (ser_bound, de_bound) = try!(get_where_predicates(cx, meta_items));
|
let (ser, de) = try!(get_where_predicates(cx, meta_items));
|
||||||
if ser_bound.is_some() {
|
ser_bound.set_opt(ser);
|
||||||
container_attrs.ser_bound = ser_bound;
|
de_bound.set_opt(de);
|
||||||
}
|
|
||||||
if de_bound.is_some() {
|
|
||||||
container_attrs.de_bound = de_bound;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
@ -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 {
|
pub fn name(&self) -> &Name {
|
||||||
@ -160,29 +212,25 @@ pub struct VariantAttrs {
|
|||||||
|
|
||||||
impl VariantAttrs {
|
impl VariantAttrs {
|
||||||
pub fn from_variant(cx: &ExtCtxt, variant: &ast::Variant) -> Result<Self, Error> {
|
pub fn from_variant(cx: &ExtCtxt, variant: &ast::Variant) -> Result<Self, Error> {
|
||||||
let mut variant_attrs = VariantAttrs {
|
let mut ser_name = Attr::none(cx, "rename");
|
||||||
name: Name::new(variant.node.name),
|
let mut de_name = Attr::none(cx, "rename");
|
||||||
};
|
|
||||||
|
|
||||||
for meta_items in variant.node.attrs.iter().filter_map(get_serde_meta_items) {
|
for meta_items in variant.node.attrs.iter().filter_map(get_serde_meta_items) {
|
||||||
for meta_item in meta_items {
|
for meta_item in meta_items {
|
||||||
|
let span = meta_item.span;
|
||||||
match meta_item.node {
|
match meta_item.node {
|
||||||
// Parse `#[serde(rename="foo")]`
|
// Parse `#[serde(rename="foo")]`
|
||||||
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"rename" => {
|
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"rename" => {
|
||||||
let s = try!(get_str_from_lit(cx, name, lit));
|
let s = try!(get_str_from_lit(cx, name, lit));
|
||||||
variant_attrs.name.serialize_name = Some(s.clone());
|
ser_name.set(span, s.clone());
|
||||||
variant_attrs.name.deserialize_name = Some(s);
|
de_name.set(span, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse `#[serde(rename(serialize="foo", deserialize="bar"))]`
|
// Parse `#[serde(rename(serialize="foo", deserialize="bar"))]`
|
||||||
ast::MetaItemKind::List(ref name, ref meta_items) if name == &"rename" => {
|
ast::MetaItemKind::List(ref name, ref meta_items) if name == &"rename" => {
|
||||||
let (ser_name, de_name) = try!(get_renames(cx, meta_items));
|
let (ser, de) = try!(get_renames(cx, meta_items));
|
||||||
if ser_name.is_some() {
|
ser_name.set_opt(ser);
|
||||||
variant_attrs.name.serialize_name = ser_name;
|
de_name.set_opt(de);
|
||||||
}
|
|
||||||
if de_name.is_some() {
|
|
||||||
variant_attrs.name.deserialize_name = de_name;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
@ -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 {
|
pub fn name(&self) -> &Name {
|
||||||
@ -209,8 +263,8 @@ impl VariantAttrs {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct FieldAttrs {
|
pub struct FieldAttrs {
|
||||||
name: Name,
|
name: Name,
|
||||||
skip_serializing_field: bool,
|
skip_serializing: bool,
|
||||||
skip_deserializing_field: bool,
|
skip_deserializing: bool,
|
||||||
skip_serializing_if: Option<ast::Path>,
|
skip_serializing_if: Option<ast::Path>,
|
||||||
default: FieldDefault,
|
default: FieldDefault,
|
||||||
serialize_with: Option<ast::Path>,
|
serialize_with: Option<ast::Path>,
|
||||||
@ -235,107 +289,91 @@ impl FieldAttrs {
|
|||||||
pub fn from_field(cx: &ExtCtxt,
|
pub fn from_field(cx: &ExtCtxt,
|
||||||
index: usize,
|
index: usize,
|
||||||
field: &ast::StructField) -> Result<Self, Error> {
|
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 {
|
let field_ident = match field.ident {
|
||||||
Some(ident) => ident,
|
Some(ident) => ident,
|
||||||
None => builder.id(index.to_string()),
|
None => index.to_string().to_ident(),
|
||||||
};
|
|
||||||
|
|
||||||
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,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
for meta_items in field.attrs.iter().filter_map(get_serde_meta_items) {
|
for meta_items in field.attrs.iter().filter_map(get_serde_meta_items) {
|
||||||
for meta_item in meta_items {
|
for meta_item in meta_items {
|
||||||
|
let span = meta_item.span;
|
||||||
match meta_item.node {
|
match meta_item.node {
|
||||||
// Parse `#[serde(rename="foo")]`
|
// Parse `#[serde(rename="foo")]`
|
||||||
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"rename" => {
|
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"rename" => {
|
||||||
let s = try!(get_str_from_lit(cx, name, lit));
|
let s = try!(get_str_from_lit(cx, name, lit));
|
||||||
field_attrs.name.serialize_name = Some(s.clone());
|
ser_name.set(span, s.clone());
|
||||||
field_attrs.name.deserialize_name = Some(s);
|
de_name.set(span, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse `#[serde(rename(serialize="foo", deserialize="bar"))]`
|
// Parse `#[serde(rename(serialize="foo", deserialize="bar"))]`
|
||||||
ast::MetaItemKind::List(ref name, ref meta_items) if name == &"rename" => {
|
ast::MetaItemKind::List(ref name, ref meta_items) if name == &"rename" => {
|
||||||
let (ser_name, de_name) = try!(get_renames(cx, meta_items));
|
let (ser, de) = try!(get_renames(cx, meta_items));
|
||||||
if ser_name.is_some() {
|
ser_name.set_opt(ser);
|
||||||
field_attrs.name.serialize_name = ser_name;
|
de_name.set_opt(de);
|
||||||
}
|
|
||||||
if de_name.is_some() {
|
|
||||||
field_attrs.name.deserialize_name = de_name;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse `#[serde(default)]`
|
// Parse `#[serde(default)]`
|
||||||
ast::MetaItemKind::Word(ref name) if name == &"default" => {
|
ast::MetaItemKind::Word(ref name) if name == &"default" => {
|
||||||
field_attrs.default = FieldDefault::Default;
|
default.set(span, FieldDefault::Default);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse `#[serde(default="...")]`
|
// Parse `#[serde(default="...")]`
|
||||||
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"default" => {
|
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"default" => {
|
||||||
let path = try!(parse_lit_into_path(cx, name, lit));
|
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)]`
|
// Parse `#[serde(skip_serializing)]`
|
||||||
ast::MetaItemKind::Word(ref name) if name == &"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)]`
|
// Parse `#[serde(skip_deserializing)]`
|
||||||
ast::MetaItemKind::Word(ref name) if name == &"skip_deserializing" => {
|
ast::MetaItemKind::Word(ref name) if name == &"skip_deserializing" => {
|
||||||
field_attrs.skip_deserializing_field = true;
|
skip_deserializing.set_true(span);
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse `#[serde(skip_serializing_if="...")]`
|
// Parse `#[serde(skip_serializing_if="...")]`
|
||||||
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"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));
|
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="...")]`
|
// Parse `#[serde(serialize_with="...")]`
|
||||||
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"serialize_with" => {
|
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"serialize_with" => {
|
||||||
let path = try!(parse_lit_into_path(cx, name, lit));
|
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="...")]`
|
// Parse `#[serde(deserialize_with="...")]`
|
||||||
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"deserialize_with" => {
|
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"deserialize_with" => {
|
||||||
let path = try!(parse_lit_into_path(cx, name, lit));
|
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")]`
|
// Parse `#[serde(bound="D: Serialize")]`
|
||||||
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"bound" => {
|
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"bound" => {
|
||||||
let where_predicates = try!(parse_lit_into_where(cx, name, lit));
|
let where_predicates = try!(parse_lit_into_where(cx, name, lit));
|
||||||
field_attrs.ser_bound = Some(where_predicates.clone());
|
ser_bound.set(span, where_predicates.clone());
|
||||||
field_attrs.de_bound = Some(where_predicates);
|
de_bound.set(span, where_predicates);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse `#[serde(bound(serialize="D: Serialize", deserialize="D: Deserialize"))]`
|
// Parse `#[serde(bound(serialize="D: Serialize", deserialize="D: Deserialize"))]`
|
||||||
ast::MetaItemKind::List(ref name, ref meta_items) if name == &"bound" => {
|
ast::MetaItemKind::List(ref name, ref meta_items) if name == &"bound" => {
|
||||||
let (ser_bound, de_bound) = try!(get_where_predicates(cx, meta_items));
|
let (ser, de) = try!(get_where_predicates(cx, meta_items));
|
||||||
if ser_bound.is_some() {
|
ser_bound.set_opt(ser);
|
||||||
field_attrs.ser_bound = ser_bound;
|
de_bound.set_opt(de);
|
||||||
}
|
|
||||||
if de_bound.is_some() {
|
|
||||||
field_attrs.de_bound = de_bound;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
@ -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 {
|
pub fn name(&self) -> &Name {
|
||||||
&self.name
|
&self.name
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn skip_serializing_field(&self) -> bool {
|
pub fn skip_serializing(&self) -> bool {
|
||||||
self.skip_serializing_field
|
self.skip_serializing
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn skip_deserializing_field(&self) -> bool {
|
pub fn skip_deserializing(&self) -> bool {
|
||||||
self.skip_deserializing_field
|
self.skip_deserializing
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn skip_serializing_if(&self) -> Option<&ast::Path> {
|
pub fn skip_serializing_if(&self) -> Option<&ast::Path> {
|
||||||
@ -410,7 +468,7 @@ fn get_ser_and_de<T, F>(
|
|||||||
attribute: &str,
|
attribute: &str,
|
||||||
items: &[P<ast::MetaItem>],
|
items: &[P<ast::MetaItem>],
|
||||||
f: F
|
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>,
|
where F: Fn(&ExtCtxt, &str, &ast::Lit) -> Result<T, Error>,
|
||||||
{
|
{
|
||||||
let mut ser_item = None;
|
let mut ser_item = None;
|
||||||
@ -420,12 +478,12 @@ fn get_ser_and_de<T, F>(
|
|||||||
match item.node {
|
match item.node {
|
||||||
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"serialize" => {
|
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"serialize" => {
|
||||||
let s = try!(f(cx, name, lit));
|
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" => {
|
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"deserialize" => {
|
||||||
let s = try!(f(cx, name, lit));
|
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(
|
fn get_renames(
|
||||||
cx: &ExtCtxt,
|
cx: &ExtCtxt,
|
||||||
items: &[P<ast::MetaItem>],
|
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)
|
get_ser_and_de(cx, "rename", items, get_str_from_lit)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_where_predicates(
|
fn get_where_predicates(
|
||||||
cx: &ExtCtxt,
|
cx: &ExtCtxt,
|
||||||
items: &[P<ast::MetaItem>],
|
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)
|
get_ser_and_de(cx, "bound", items, parse_lit_into_where)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,7 +135,7 @@ fn build_impl_generics(
|
|||||||
// attribute specify their own bound so we do not generate one. All other fields
|
// 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.
|
// may need a `T: Deserialize` bound where T is the type of the field.
|
||||||
fn needs_deserialize_bound(attrs: &attr::FieldAttrs) -> bool {
|
fn needs_deserialize_bound(attrs: &attr::FieldAttrs) -> bool {
|
||||||
!attrs.skip_deserializing_field()
|
!attrs.skip_deserializing()
|
||||||
&& attrs.deserialize_with().is_none()
|
&& attrs.deserialize_with().is_none()
|
||||||
&& attrs.de_bound().is_none()
|
&& attrs.de_bound().is_none()
|
||||||
}
|
}
|
||||||
@ -450,7 +450,7 @@ fn deserialize_seq(
|
|||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, &(ref field, ref attrs))| {
|
.map(|(i, &(ref field, ref attrs))| {
|
||||||
let name = builder.id(format!("__field{}", i));
|
let name = builder.id(format!("__field{}", i));
|
||||||
if attrs.skip_deserializing_field() {
|
if attrs.skip_deserializing() {
|
||||||
let default = expr_is_missing(cx, attrs);
|
let default = expr_is_missing(cx, attrs);
|
||||||
quote_stmt!(cx,
|
quote_stmt!(cx,
|
||||||
let $name = $default;
|
let $name = $default;
|
||||||
@ -1049,7 +1049,7 @@ fn deserialize_map(
|
|||||||
|
|
||||||
// Declare each field that will be deserialized.
|
// Declare each field that will be deserialized.
|
||||||
let let_values: Vec<ast::Stmt> = fields_attrs_names.iter()
|
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)| {
|
.map(|&(field, _, name)| {
|
||||||
let field_ty = &field.ty;
|
let field_ty = &field.ty;
|
||||||
quote_stmt!(cx, let mut $name: Option<$field_ty> = None;).unwrap()
|
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.
|
// Match arms to extract a value for a field.
|
||||||
let value_arms = fields_attrs_names.iter()
|
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)| {
|
.map(|&(ref field, ref attrs, name)| {
|
||||||
let deser_name = attrs.name().deserialize_name();
|
let deser_name = attrs.name().deserialize_name();
|
||||||
let name_str = builder.expr().lit().str(deser_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`.
|
// Match arms to ignore value for fields that have `skip_deserializing`.
|
||||||
// Ignored even if `deny_unknown_fields` is set.
|
// Ignored even if `deny_unknown_fields` is set.
|
||||||
let skipped_arms = fields_attrs_names.iter()
|
let skipped_arms = fields_attrs_names.iter()
|
||||||
.filter(|&&(_, ref attrs, _)| attrs.skip_deserializing_field())
|
.filter(|&&(_, ref attrs, _)| attrs.skip_deserializing())
|
||||||
.map(|&(_, _, name)| {
|
.map(|&(_, _, name)| {
|
||||||
quote_arm!(cx,
|
quote_arm!(cx,
|
||||||
__Field::$name => {
|
__Field::$name => {
|
||||||
@ -1112,7 +1112,7 @@ fn deserialize_map(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let extract_values = fields_attrs_names.iter()
|
let extract_values = fields_attrs_names.iter()
|
||||||
.filter(|&&(_, ref attrs, _)| !attrs.skip_deserializing_field())
|
.filter(|&&(_, ref attrs, _)| !attrs.skip_deserializing())
|
||||||
.map(|&(_, ref attrs, name)| {
|
.map(|&(_, ref attrs, name)| {
|
||||||
let missing_expr = expr_is_missing(cx, attrs);
|
let missing_expr = expr_is_missing(cx, attrs);
|
||||||
|
|
||||||
@ -1138,7 +1138,7 @@ fn deserialize_map(
|
|||||||
cx.span_bug(field.span, "struct contains unnamed fields")
|
cx.span_bug(field.span, "struct contains unnamed fields")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
if attrs.skip_deserializing_field() {
|
if attrs.skip_deserializing() {
|
||||||
expr_is_missing(cx, attrs)
|
expr_is_missing(cx, attrs)
|
||||||
} else {
|
} else {
|
||||||
builder.expr().id(name)
|
builder.expr().id(name)
|
||||||
@ -1258,7 +1258,7 @@ fn check_no_str(
|
|||||||
};
|
};
|
||||||
|
|
||||||
for &(ref field, ref attrs) in fields {
|
for &(ref field, ref attrs) in fields {
|
||||||
if attrs.skip_deserializing_field()
|
if attrs.skip_deserializing()
|
||||||
|| attrs.deserialize_with().is_some() { continue }
|
|| attrs.deserialize_with().is_some() { continue }
|
||||||
|
|
||||||
if let ast::TyKind::Rptr(_, ref inner) = field.ty.node {
|
if let ast::TyKind::Rptr(_, ref inner) = field.ty.node {
|
||||||
|
@ -129,7 +129,7 @@ fn build_impl_generics(
|
|||||||
// attribute specify their own bound so we do not generate one. All other fields
|
// 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.
|
// may need a `T: Serialize` bound where T is the type of the field.
|
||||||
fn needs_serialize_bound(attrs: &attr::FieldAttrs) -> bool {
|
fn needs_serialize_bound(attrs: &attr::FieldAttrs) -> bool {
|
||||||
!attrs.skip_serializing_field()
|
!attrs.skip_serializing()
|
||||||
&& attrs.serialize_with().is_none()
|
&& attrs.serialize_with().is_none()
|
||||||
&& attrs.ser_bound().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 fields_with_attrs = try!(attr::fields_with_attrs(cx, fields));
|
||||||
|
|
||||||
let arms: Vec<ast::Arm> = fields_with_attrs.iter()
|
let arms: Vec<ast::Arm> = fields_with_attrs.iter()
|
||||||
.filter(|&&(_, ref attrs)| !attrs.skip_serializing_field())
|
.filter(|&&(_, ref attrs)| !attrs.skip_serializing())
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, &(ref field, ref attrs))| {
|
.map(|(i, &(ref field, ref attrs))| {
|
||||||
let ident = field.ident.expect("struct has unnamed field");
|
let ident = field.ident.expect("struct has unnamed field");
|
||||||
@ -794,7 +794,7 @@ fn serialize_struct_visitor(
|
|||||||
.build();
|
.build();
|
||||||
|
|
||||||
let len = fields_with_attrs.iter()
|
let len = fields_with_attrs.iter()
|
||||||
.filter(|&&(_, ref attrs)| !attrs.skip_serializing_field())
|
.filter(|&&(_, ref attrs)| !attrs.skip_serializing())
|
||||||
.map(|&(ref field, ref attrs)| {
|
.map(|&(ref field, ref attrs)| {
|
||||||
let ident = field.ident.expect("struct has unnamed fields");
|
let ident = field.ident.expect("struct has unnamed fields");
|
||||||
let mut field_expr = quote_expr!(cx, self.value.$ident);
|
let mut field_expr = quote_expr!(cx, self.value.$ident);
|
||||||
|
25
serde_macros/tests/compile-fail/duplicate_attributes.rs
Normal file
25
serde_macros/tests/compile-fail/duplicate_attributes.rs
Normal 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() {}
|
@ -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() {}
|
|
Loading…
x
Reference in New Issue
Block a user