diff --git a/serde_derive_internals/src/ast.rs b/serde_derive_internals/src/ast.rs index 818d301b..dce1acc1 100644 --- a/serde_derive_internals/src/ast.rs +++ b/serde_derive_internals/src/ast.rs @@ -51,7 +51,7 @@ impl<'a> Container<'a> { let mut body = match item.body { syn::Body::Enum(ref variants) => Body::Enum(enum_from_ast(cx, variants)), syn::Body::Struct(ref variant_data) => { - let (style, fields) = struct_from_ast(cx, variant_data); + let (style, fields) = struct_from_ast(cx, variant_data, None); Body::Struct(style, fields) } }; @@ -103,10 +103,11 @@ fn enum_from_ast<'a>(cx: &Ctxt, variants: &'a [syn::Variant]) -> Vec .iter() .map( |variant| { - let (style, fields) = struct_from_ast(cx, &variant.data); + let attrs = attr::Variant::from_ast(cx, variant); + let (style, fields) = struct_from_ast(cx, &variant.data, Some(&attrs)); Variant { ident: variant.ident.clone(), - attrs: attr::Variant::from_ast(cx, variant), + attrs: attrs, style: style, fields: fields, } @@ -115,18 +116,18 @@ fn enum_from_ast<'a>(cx: &Ctxt, variants: &'a [syn::Variant]) -> Vec .collect() } -fn struct_from_ast<'a>(cx: &Ctxt, data: &'a syn::VariantData) -> (Style, Vec>) { +fn struct_from_ast<'a>(cx: &Ctxt, data: &'a syn::VariantData, attrs: Option<&attr::Variant>) -> (Style, Vec>) { match *data { - syn::VariantData::Struct(ref fields) => (Style::Struct, fields_from_ast(cx, fields)), + syn::VariantData::Struct(ref fields) => (Style::Struct, fields_from_ast(cx, fields, attrs)), syn::VariantData::Tuple(ref fields) if fields.len() == 1 => { - (Style::Newtype, fields_from_ast(cx, fields)) + (Style::Newtype, fields_from_ast(cx, fields, attrs)) } - syn::VariantData::Tuple(ref fields) => (Style::Tuple, fields_from_ast(cx, fields)), + syn::VariantData::Tuple(ref fields) => (Style::Tuple, fields_from_ast(cx, fields, attrs)), syn::VariantData::Unit => (Style::Unit, Vec::new()), } } -fn fields_from_ast<'a>(cx: &Ctxt, fields: &'a [syn::Field]) -> Vec> { +fn fields_from_ast<'a>(cx: &Ctxt, fields: &'a [syn::Field], attrs: Option<&attr::Variant>) -> Vec> { fields .iter() .enumerate() @@ -134,7 +135,7 @@ fn fields_from_ast<'a>(cx: &Ctxt, fields: &'a [syn::Field]) -> Vec> { |(i, field)| { Field { ident: field.ident.clone(), - attrs: attr::Field::from_ast(cx, i, field), + attrs: attr::Field::from_ast(cx, i, field, attrs), ty: &field.ty, } }, diff --git a/serde_derive_internals/src/attr.rs b/serde_derive_internals/src/attr.rs index 454b8b59..7baeb59b 100644 --- a/serde_derive_internals/src/attr.rs +++ b/serde_derive_internals/src/attr.rs @@ -512,6 +512,7 @@ pub struct Variant { other: bool, serialize_with: Option, deserialize_with: Option, + borrow: Option, } impl Variant { @@ -524,6 +525,7 @@ impl Variant { let mut other = BoolAttr::none(cx, "other"); let mut serialize_with = Attr::none(cx, "serialize_with"); let mut deserialize_with = Attr::none(cx, "deserialize_with"); + let mut borrow = Attr::none(cx, "borrow"); for meta_items in variant.attrs.iter().filter_map(get_serde_meta_items) { for meta_item in meta_items { @@ -599,6 +601,18 @@ impl Variant { } } + // Defer `#[serde(borrow)]` and `#[serde(borrow = "'a + 'b")]` + MetaItem(ref mi) if mi.name() == "borrow" => { + match variant.data { + syn::VariantData::Tuple(ref fields) if fields.len() == 1 => { + borrow.set(mi.clone()); + } + _ => { + cx.error("#[serde(borrow)] may only be used on newtype variants"); + } + } + } + MetaItem(ref meta_item) => { cx.error(format!("unknown serde variant attribute `{}`", meta_item.name())); } @@ -627,6 +641,7 @@ impl Variant { other: other.get(), serialize_with: serialize_with.get(), deserialize_with: deserialize_with.get(), + borrow: borrow.get(), } } @@ -699,7 +714,7 @@ pub enum Default { impl Field { /// Extract out the `#[serde(...)]` attributes from a struct field. - pub fn from_ast(cx: &Ctxt, index: usize, field: &syn::Field) -> Self { + pub fn from_ast(cx: &Ctxt, index: usize, field: &syn::Field, attrs: Option<&Variant>) -> Self { 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"); @@ -718,7 +733,13 @@ impl Field { None => index.to_string(), }; - for meta_items in field.attrs.iter().filter_map(get_serde_meta_items) { + let variant_borrow = attrs + .map(|variant| &variant.borrow) + .unwrap_or(&None) + .as_ref() + .map(|borrow| vec![MetaItem(borrow.clone())]); + + for meta_items in field.attrs.iter().filter_map(get_serde_meta_items).chain(variant_borrow) { for meta_item in meta_items { match meta_item { // Parse `#[serde(rename = "foo")]` diff --git a/test_suite/tests/compile-fail/borrow/duplicate_variant.rs b/test_suite/tests/compile-fail/borrow/duplicate_variant.rs new file mode 100644 index 00000000..5cc3ad3b --- /dev/null +++ b/test_suite/tests/compile-fail/borrow/duplicate_variant.rs @@ -0,0 +1,21 @@ +// Copyright 2017 Serde Developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate serde_derive; + +#[derive(Deserialize)] +struct Str<'a>(&'a str); + +#[derive(Deserialize)] //~ ERROR: proc-macro derive panicked +enum Test<'a> { + #[serde(borrow)] //~^^ HELP: duplicate serde attribute `borrow` + S(#[serde(borrow)] Str<'a>) +} + +fn main() {} diff --git a/test_suite/tests/compile-fail/borrow/struct_variant.rs b/test_suite/tests/compile-fail/borrow/struct_variant.rs new file mode 100644 index 00000000..5bab0717 --- /dev/null +++ b/test_suite/tests/compile-fail/borrow/struct_variant.rs @@ -0,0 +1,21 @@ +// Copyright 2017 Serde Developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate serde_derive; + +#[derive(Deserialize)] +struct Str<'a>(&'a str); + +#[derive(Deserialize)] //~ ERROR: proc-macro derive panicked +enum Test<'a> { + #[serde(borrow)] //~^^ HELP: #[serde(borrow)] may only be used on newtype variants + S { s: Str<'a> } +} + +fn main() {} diff --git a/test_suite/tests/test_gen.rs b/test_suite/tests/test_gen.rs index 50cd13b7..515c2f82 100644 --- a/test_suite/tests/test_gen.rs +++ b/test_suite/tests/test_gen.rs @@ -357,6 +357,12 @@ fn test_gen() { s: Str<'a>, } + #[derive(Serialize, Deserialize)] + enum BorrowVariant<'a> { + #[serde(borrow, with = "StrDef")] + S(Str<'a>), + } + mod vis { pub struct S;