diff --git a/serde_derive/src/de.rs b/serde_derive/src/de.rs index ebc9fb4b..22bd034d 100644 --- a/serde_derive/src/de.rs +++ b/serde_derive/src/de.rs @@ -1140,25 +1140,17 @@ fn deserialize_enum( } } -fn deserialize_externally_tagged_enum( - params: &Parameters, +fn prepare_enum_variant_enum( variants: &[Variant], cattrs: &attr::Container, -) -> Fragment { - let this = ¶ms.this; - let (de_impl_generics, de_ty_generics, ty_generics, where_clause) = - split_with_de_lifetime(params); - let delife = params.borrowed.de_lifetime(); - - let type_name = cattrs.name().deserialize_name(); - - let expecting = format!("enum {}", params.type_name()); - +) -> (proc_macro2::TokenStream, Stmts) { let variant_names_idents: Vec<_> = variants .iter() .enumerate() .filter(|&(_, variant)| !variant.attrs.skip_deserializing()) - .map(|(i, variant)| (variant.attrs.name().deserialize_name(), field_i(i))) + .map(|(i, variant)| { + (variant.attrs.name().deserialize_name(), field_i(i), variant.attrs.aliases()) + }) .collect(); let other_idx = variants @@ -1166,7 +1158,7 @@ fn deserialize_externally_tagged_enum( .position(|ref variant| variant.attrs.other()); let variants_stmt = { - let variant_names = variant_names_idents.iter().map(|&(ref name, _)| name); + let variant_names = variant_names_idents.iter().map(|&(ref name, _, _)| name); quote! { const VARIANTS: &'static [&'static str] = &[ #(#variant_names),* ]; } @@ -1179,6 +1171,24 @@ fn deserialize_externally_tagged_enum( other_idx, )); + (variants_stmt, variant_visitor) +} + +fn deserialize_externally_tagged_enum( + params: &Parameters, + variants: &[Variant], + cattrs: &attr::Container, +) -> Fragment { + let this = ¶ms.this; + let (de_impl_generics, de_ty_generics, ty_generics, where_clause) = + split_with_de_lifetime(params); + let delife = params.borrowed.de_lifetime(); + + let type_name = cattrs.name().deserialize_name(); + let expecting = format!("enum {}", params.type_name()); + + let (variants_stmt, variant_visitor) = prepare_enum_variant_enum(variants, cattrs); + // Match arms to extract a variant from a string let variant_arms = variants .iter() @@ -1261,30 +1271,7 @@ fn deserialize_internally_tagged_enum( cattrs: &attr::Container, tag: &str, ) -> Fragment { - let variant_names_idents: Vec<_> = variants - .iter() - .enumerate() - .filter(|&(_, variant)| !variant.attrs.skip_deserializing()) - .map(|(i, variant)| (variant.attrs.name().deserialize_name(), field_i(i))) - .collect(); - - let other_idx = variants - .iter() - .position(|ref variant| variant.attrs.other()); - - let variants_stmt = { - let variant_names = variant_names_idents.iter().map(|&(ref name, _)| name); - quote! { - const VARIANTS: &'static [&'static str] = &[ #(#variant_names),* ]; - } - }; - - let variant_visitor = Stmts(deserialize_generated_identifier( - &variant_names_idents, - cattrs, - true, - other_idx, - )); + let (variants_stmt, variant_visitor) = prepare_enum_variant_enum(variants, cattrs); // Match arms to extract a variant from a string let variant_arms = variants @@ -1335,30 +1322,7 @@ fn deserialize_adjacently_tagged_enum( split_with_de_lifetime(params); let delife = params.borrowed.de_lifetime(); - let variant_names_idents: Vec<_> = variants - .iter() - .enumerate() - .filter(|&(_, variant)| !variant.attrs.skip_deserializing()) - .map(|(i, variant)| (variant.attrs.name().deserialize_name(), field_i(i))) - .collect(); - - let other_idx = variants - .iter() - .position(|ref variant| variant.attrs.other()); - - let variants_stmt = { - let variant_names = variant_names_idents.iter().map(|&(ref name, _)| name); - quote! { - const VARIANTS: &'static [&'static str] = &[ #(#variant_names),* ]; - } - }; - - let variant_visitor = Stmts(deserialize_generated_identifier( - &variant_names_idents, - cattrs, - true, - other_idx, - )); + let (variants_stmt, variant_visitor) = prepare_enum_variant_enum(variants, cattrs); let variant_arms: &Vec<_> = &variants .iter() @@ -1870,13 +1834,13 @@ fn deserialize_untagged_newtype_variant( } fn deserialize_generated_identifier( - fields: &[(String, Ident)], + fields: &[(String, Ident, Vec)], cattrs: &attr::Container, is_variant: bool, other_idx: Option, ) -> Fragment { let this = quote!(__Field); - let field_idents: &Vec<_> = &fields.iter().map(|&(_, ref ident)| ident).collect(); + let field_idents: &Vec<_> = &fields.iter().map(|&(_, ref ident, _)| ident).collect(); let (ignore_variant, fallthrough) = if !is_variant && cattrs.has_flatten() { let ignore_variant = quote!(__other(_serde::private::de::Content<'de>),); @@ -1977,11 +1941,12 @@ fn deserialize_custom_identifier( ( variant.attrs.name().deserialize_name(), variant.ident.clone(), + variant.attrs.aliases(), ) }) .collect(); - let names = names_idents.iter().map(|&(ref name, _)| name); + let names = names_idents.iter().map(|&(ref name, _, _)| name); let names_const = if fallthrough.is_some() { None @@ -2032,24 +1997,33 @@ fn deserialize_custom_identifier( fn deserialize_identifier( this: &TokenStream, - fields: &[(String, Ident)], + fields: &[(String, Ident, Vec)], is_variant: bool, fallthrough: Option, collect_other_fields: bool, ) -> Fragment { - let field_strs = fields.iter().map(|&(ref name, _)| name); - let field_borrowed_strs = fields.iter().map(|&(ref name, _)| name); - let field_bytes = fields + let mut flat_fields = Vec::new(); + for &(_, ref ident, ref aliases) in fields { + flat_fields.extend(aliases.into_iter().map(|alias| (alias, ident))) + } + + let field_strs = flat_fields.iter().map(|&(ref name, _)| name); + let field_borrowed_strs = flat_fields.iter().map(|&(ref name, _)| name); + let field_bytes = flat_fields .iter() .map(|&(ref name, _)| Literal::byte_string(name.as_bytes())); - let field_borrowed_bytes = fields + let field_borrowed_bytes = flat_fields .iter() .map(|&(ref name, _)| Literal::byte_string(name.as_bytes())); - let constructors: &Vec<_> = &fields + let constructors: &Vec<_> = &flat_fields .iter() .map(|&(_, ref ident)| quote!(#this::#ident)) .collect(); + let main_constructors: &Vec<_> = &fields + .iter() + .map(|&(_, ref ident, _)| quote!(#this::#ident)) + .collect(); let expecting = if is_variant { "variant identifier" @@ -2237,7 +2211,7 @@ fn deserialize_identifier( { match __value { #( - #variant_indices => _serde::export::Ok(#constructors), + #variant_indices => _serde::export::Ok(#main_constructors), )* _ => _serde::export::Err(_serde::de::Error::invalid_value( _serde::de::Unexpected::Unsigned(__value), @@ -2300,11 +2274,13 @@ fn deserialize_struct_as_struct_visitor( .iter() .enumerate() .filter(|&(_, field)| !field.attrs.skip_deserializing()) - .map(|(i, field)| (field.attrs.name().deserialize_name(), field_i(i))) + .map(|(i, field)| { + (field.attrs.name().deserialize_name(), field_i(i), field.attrs.aliases()) + }) .collect(); let fields_stmt = { - let field_names = field_names_idents.iter().map(|&(ref name, _)| name); + let field_names = field_names_idents.iter().map(|&(ref name, _, _)| name); quote_block! { const FIELDS: &'static [&'static str] = &[ #(#field_names),* ]; } @@ -2327,7 +2303,9 @@ fn deserialize_struct_as_map_visitor( .iter() .enumerate() .filter(|&(_, field)| !field.attrs.skip_deserializing() && !field.attrs.flatten()) - .map(|(i, field)| (field.attrs.name().deserialize_name(), field_i(i))) + .map(|(i, field)| { + (field.attrs.name().deserialize_name(), field_i(i), field.attrs.aliases()) + }) .collect(); let field_visitor = deserialize_generated_identifier(&field_names_idents, cattrs, false, None); @@ -2558,11 +2536,13 @@ fn deserialize_struct_as_struct_in_place_visitor( .iter() .enumerate() .filter(|&(_, field)| !field.attrs.skip_deserializing()) - .map(|(i, field)| (field.attrs.name().deserialize_name(), field_i(i))) + .map(|(i, field)| { + (field.attrs.name().deserialize_name(), field_i(i), field.attrs.aliases()) + }) .collect(); let fields_stmt = { - let field_names = field_names_idents.iter().map(|&(ref name, _)| name); + let field_names = field_names_idents.iter().map(|&(ref name, _, _)| name); quote_block! { const FIELDS: &'static [&'static str] = &[ #(#field_names),* ]; } diff --git a/serde_derive/src/internals/attr.rs b/serde_derive/src/internals/attr.rs index f6ff5aed..63a4dc3a 100644 --- a/serde_derive/src/internals/attr.rs +++ b/serde_derive/src/internals/attr.rs @@ -90,9 +90,52 @@ impl<'c> BoolAttr<'c> { } } +struct VecAttr<'c, T> { + cx: &'c Ctxt, + name: &'static str, + first_dup_tokens: TokenStream, + values: Vec, +} + +impl<'c, T> VecAttr<'c, T> { + fn none(cx: &'c Ctxt, name: &'static str) -> Self { + VecAttr { + cx: cx, + name: name, + first_dup_tokens: TokenStream::new(), + values: Vec::new(), + } + } + + fn insert(&mut self, obj: A, value: T) { + if self.values.len() == 1 { + self.first_dup_tokens = obj.into_token_stream(); + } + self.values.push(value); + } + + fn at_most_one(mut self) -> Result, ()> { + if self.values.len() > 1 { + let dup_token = self.first_dup_tokens; + self.cx + .error_spanned_by(dup_token, format!("duplicate serde attribute `{}`", self.name)); + Err(()) + } else { + Ok(self.values.drain(..).next()) + } + } + + fn get(self) -> Vec { + self.values + } +} + pub struct Name { serialize: String, + serialize_renamed: bool, deserialize: String, + deserialize_renamed: bool, + deserialize_aliases: Vec, } #[allow(deprecated)] @@ -104,6 +147,36 @@ fn unraw(ident: &Ident) -> String { } impl Name { + fn from_attrs( + source_name: String, + ser_name: Attr, + de_name: Attr, + de_aliases: Option>, + ) -> Name { + let deserialize_aliases = match de_aliases { + Some(de_aliases) => { + let mut alias_list = BTreeSet::new(); + for alias_name in de_aliases.get() { + alias_list.insert(alias_name); + } + alias_list.into_iter().collect() + } + None => Vec::new(), + }; + + let ser_name = ser_name.get(); + let ser_renamed = ser_name.is_some(); + let de_name = de_name.get(); + let de_renamed = de_name.is_some(); + Name { + serialize: ser_name.unwrap_or_else(|| source_name.clone()), + serialize_renamed: ser_renamed, + deserialize: de_name.unwrap_or(source_name), + deserialize_renamed: de_renamed, + deserialize_aliases: deserialize_aliases, + } + } + /// Return the container name for the container when serializing. pub fn serialize_name(&self) -> String { self.serialize.clone() @@ -113,6 +186,15 @@ impl Name { pub fn deserialize_name(&self) -> String { self.deserialize.clone() } + + fn deserialize_aliases(&self) -> Vec { + let mut aliases = self.deserialize_aliases.clone(); + let main_name = self.deserialize_name(); + if !aliases.contains(&main_name) { + aliases.push(main_name); + } + aliases + } } pub struct RenameAllRules { @@ -514,10 +596,7 @@ impl Container { } Container { - name: Name { - serialize: ser_name.get().unwrap_or_else(|| unraw(&item.ident)), - deserialize: de_name.get().unwrap_or_else(|| unraw(&item.ident)), - }, + name: Name::from_attrs(unraw(&item.ident), ser_name, de_name, None), transparent: transparent.get(), deny_unknown_fields: deny_unknown_fields.get(), default: default.get().unwrap_or(Default::None), @@ -762,8 +841,6 @@ fn decide_identifier( /// Represents variant attribute information pub struct Variant { name: Name, - ser_renamed: bool, - de_renamed: bool, rename_all_rules: RenameAllRules, ser_bound: Option>, de_bound: Option>, @@ -779,6 +856,7 @@ impl Variant { pub fn from_ast(cx: &Ctxt, variant: &syn::Variant) -> Self { let mut ser_name = Attr::none(cx, "rename"); let mut de_name = Attr::none(cx, "rename"); + let mut de_alises = VecAttr::none(cx, "rename"); let mut skip_deserializing = BoolAttr::none(cx, "skip_deserializing"); let mut skip_serializing = BoolAttr::none(cx, "skip_serializing"); let mut rename_all_ser_rule = Attr::none(cx, "rename_all"); @@ -797,15 +875,26 @@ impl Variant { Meta(NameValue(ref m)) if m.ident == "rename" => { if let Ok(s) = get_lit_str(cx, &m.ident, &m.ident, &m.lit) { ser_name.set(&m.ident, s.value()); - de_name.set(&m.ident, s.value()); + de_name.set_if_none(s.value()); + de_alises.insert(&m.ident, s.value()); } } // Parse `#[serde(rename(serialize = "foo", deserialize = "bar"))]` Meta(List(ref m)) if m.ident == "rename" => { - if let Ok((ser, de)) = get_renames(cx, &m.nested) { + if let Ok((ser, de)) = get_multiple_renames(cx, &m.nested) { ser_name.set_opt(&m.ident, ser.map(syn::LitStr::value)); - de_name.set_opt(&m.ident, de.map(syn::LitStr::value)); + for de_value in de { + de_name.set_if_none(de_value.value()); + de_alises.insert(&m.ident, de_value.value()); + } + } + } + + // Parse `#[serde(alias = "foo")]` + Meta(NameValue(ref m)) if m.ident == "alias" => { + if let Ok(s) = get_lit_str(cx, &m.ident, &m.ident, &m.lit) { + de_alises.insert(&m.ident, s.value()); } } @@ -963,17 +1052,8 @@ impl Variant { } } - let ser_name = ser_name.get(); - let ser_renamed = ser_name.is_some(); - let de_name = de_name.get(); - let de_renamed = de_name.is_some(); Variant { - name: Name { - serialize: ser_name.unwrap_or_else(|| unraw(&variant.ident)), - deserialize: de_name.unwrap_or_else(|| unraw(&variant.ident)), - }, - ser_renamed: ser_renamed, - de_renamed: de_renamed, + name: Name::from_attrs(unraw(&variant.ident), ser_name, de_name, Some(de_alises)), rename_all_rules: RenameAllRules { serialize: rename_all_ser_rule.get().unwrap_or(RenameRule::None), deserialize: rename_all_de_rule.get().unwrap_or(RenameRule::None), @@ -993,11 +1073,15 @@ impl Variant { &self.name } + pub fn aliases(&self) -> Vec { + self.name.deserialize_aliases() + } + pub fn rename_by_rules(&mut self, rules: &RenameAllRules) { - if !self.ser_renamed { + if !self.name.serialize_renamed { self.name.serialize = rules.serialize.apply_to_variant(&self.name.serialize); } - if !self.de_renamed { + if !self.name.deserialize_renamed { self.name.deserialize = rules.deserialize.apply_to_variant(&self.name.deserialize); } } @@ -1038,8 +1122,6 @@ impl Variant { /// Represents field attribute information pub struct Field { name: Name, - ser_renamed: bool, - de_renamed: bool, skip_serializing: bool, skip_deserializing: bool, skip_serializing_if: Option, @@ -1084,6 +1166,7 @@ impl Field { ) -> Self { let mut ser_name = Attr::none(cx, "rename"); let mut de_name = Attr::none(cx, "rename"); + let mut de_alises = VecAttr::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"); @@ -1117,15 +1200,26 @@ impl Field { Meta(NameValue(ref m)) if m.ident == "rename" => { if let Ok(s) = get_lit_str(cx, &m.ident, &m.ident, &m.lit) { ser_name.set(&m.ident, s.value()); - de_name.set(&m.ident, s.value()); + de_name.set_if_none(s.value()); + de_alises.insert(&m.ident, s.value()); } } // Parse `#[serde(rename(serialize = "foo", deserialize = "bar"))]` Meta(List(ref m)) if m.ident == "rename" => { - if let Ok((ser, de)) = get_renames(cx, &m.nested) { + if let Ok((ser, de)) = get_multiple_renames(cx, &m.nested) { ser_name.set_opt(&m.ident, ser.map(syn::LitStr::value)); - de_name.set_opt(&m.ident, de.map(syn::LitStr::value)); + for de_value in de { + de_name.set_if_none(de_value.value()); + de_alises.insert(&m.ident, de_value.value()); + } + } + } + + // Parse `#[serde(alias = "foo")]` + Meta(NameValue(ref m)) if m.ident == "alias" => { + if let Ok(s) = get_lit_str(cx, &m.ident, &m.ident, &m.lit) { + de_alises.insert(&m.ident, s.value()); } } @@ -1332,17 +1426,8 @@ impl Field { collect_lifetimes(&field.ty, &mut borrowed_lifetimes); } - let ser_name = ser_name.get(); - let ser_renamed = ser_name.is_some(); - let de_name = de_name.get(); - let de_renamed = de_name.is_some(); Field { - name: Name { - serialize: ser_name.unwrap_or_else(|| ident.clone()), - deserialize: de_name.unwrap_or(ident), - }, - ser_renamed: ser_renamed, - de_renamed: de_renamed, + name: Name::from_attrs(ident, ser_name, de_name, Some(de_alises)), skip_serializing: skip_serializing.get(), skip_deserializing: skip_deserializing.get(), skip_serializing_if: skip_serializing_if.get(), @@ -1362,11 +1447,15 @@ impl Field { &self.name } + pub fn aliases(&self) -> Vec { + self.name.deserialize_aliases() + } + pub fn rename_by_rules(&mut self, rules: &RenameAllRules) { - if !self.ser_renamed { + if !self.name.serialize_renamed { self.name.serialize = rules.serialize.apply_to_field(&self.name.serialize); } - if !self.de_renamed { + if !self.name.deserialize_renamed { self.name.deserialize = rules.deserialize.apply_to_field(&self.name.deserialize); } } @@ -1426,31 +1515,31 @@ impl Field { type SerAndDe = (Option, Option); -fn get_ser_and_de<'a, T, F>( - cx: &Ctxt, +fn get_ser_and_de<'a, 'b, T, F>( + cx: &'b Ctxt, attr_name: &'static str, metas: &'a Punctuated, f: F, -) -> Result, ()> +) -> Result<(VecAttr<'b, T>, VecAttr<'b, T>), ()> where T: 'a, F: Fn(&Ctxt, &Ident, &Ident, &'a syn::Lit) -> Result, { - let mut ser_meta = Attr::none(cx, attr_name); - let mut de_meta = Attr::none(cx, attr_name); + let mut ser_meta = VecAttr::none(cx, attr_name); + let mut de_meta = VecAttr::none(cx, attr_name); let attr_name = Ident::new(attr_name, Span::call_site()); for meta in metas { match *meta { Meta(NameValue(ref meta)) if meta.ident == "serialize" => { if let Ok(v) = f(cx, &attr_name, &meta.ident, &meta.lit) { - ser_meta.set(&meta.ident, v); + ser_meta.insert(&meta.ident, v); } } Meta(NameValue(ref meta)) if meta.ident == "deserialize" => { if let Ok(v) = f(cx, &attr_name, &meta.ident, &meta.lit) { - de_meta.set(&meta.ident, v); + de_meta.insert(&meta.ident, v); } } @@ -1468,21 +1557,31 @@ where } } - Ok((ser_meta.get(), de_meta.get())) + Ok((ser_meta, de_meta)) } fn get_renames<'a>( cx: &Ctxt, items: &'a Punctuated, ) -> Result, ()> { - get_ser_and_de(cx, "rename", items, get_lit_str) + let (ser, de) = try!(get_ser_and_de(cx, "rename", items, get_lit_str)); + Ok((try!(ser.at_most_one()), try!(de.at_most_one()))) +} + +fn get_multiple_renames<'a>( + cx: &Ctxt, + items: &'a Punctuated, +) -> Result<(Option<&'a syn::LitStr>, Vec<&'a syn::LitStr>), ()> { + let (ser, de) = try!(get_ser_and_de(cx, "rename", items, get_lit_str)); + Ok((try!(ser.at_most_one()), de.get())) } fn get_where_predicates( cx: &Ctxt, items: &Punctuated, ) -> Result>, ()> { - get_ser_and_de(cx, "bound", items, parse_lit_into_where) + let (ser, de) = try!(get_ser_and_de(cx, "bound", items, parse_lit_into_where)); + Ok((try!(ser.at_most_one()), try!(de.at_most_one()))) } pub fn get_serde_meta_items(attr: &syn::Attribute) -> Option> { diff --git a/serde_derive/src/internals/check.rs b/serde_derive/src/internals/check.rs index d349bd49..71a4bd94 100644 --- a/serde_derive/src/internals/check.rs +++ b/serde_derive/src/internals/check.rs @@ -295,12 +295,18 @@ fn check_internal_tag_field_name_conflict(cx: &Ctxt, cont: &Container) { let check_de = !field.attrs.skip_deserializing(); let name = field.attrs.name(); let ser_name = name.serialize_name(); - let de_name = name.deserialize_name(); - if check_ser && ser_name == tag || check_de && de_name == tag { + if check_ser && ser_name == tag { diagnose_conflict(); return; } + + for de_name in field.attrs.aliases() { + if check_de && de_name == tag { + diagnose_conflict(); + return; + } + } } } Style::Unit | Style::Newtype | Style::Tuple => {} diff --git a/test_suite/tests/test_annotations.rs b/test_suite/tests/test_annotations.rs index fd07dca0..595c06d8 100644 --- a/test_suite/tests/test_annotations.rs +++ b/test_suite/tests/test_annotations.rs @@ -516,6 +516,16 @@ struct RenameStructSerializeDeserialize { a2: i32, } +#[derive(Debug, PartialEq, Deserialize)] +#[serde(deny_unknown_fields)] +struct AliasStruct { + a1: i32, + #[serde(alias = "a3")] + a2: i32, + #[serde(alias = "a5", rename = "a6")] + a4: i32, +} + #[test] fn test_rename_struct() { assert_tokens( @@ -562,6 +572,59 @@ fn test_rename_struct() { Token::StructEnd, ], ); + + assert_de_tokens( + &AliasStruct { a1: 1, a2: 2, a4: 3 }, + &[ + Token::Struct { + name: "AliasStruct", + len: 3, + }, + Token::Str("a1"), + Token::I32(1), + Token::Str("a2"), + Token::I32(2), + Token::Str("a5"), + Token::I32(3), + Token::StructEnd, + ], + ); + + assert_de_tokens( + &AliasStruct { a1: 1, a2: 2, a4: 3 }, + &[ + Token::Struct { + name: "AliasStruct", + len: 3, + }, + Token::Str("a1"), + Token::I32(1), + Token::Str("a3"), + Token::I32(2), + Token::Str("a6"), + Token::I32(3), + Token::StructEnd, + ], + ); +} + +#[test] +fn test_unknown_field_rename_struct() { + assert_de_tokens_error::( + &[ + Token::Struct { + name: "AliasStruct", + len: 3, + }, + Token::Str("a1"), + Token::I32(1), + Token::Str("a3"), + Token::I32(2), + Token::Str("a4"), + Token::I32(3), + ], + "unknown field `a4`, expected one of `a1`, `a2`, `a6`", + ); } #[derive(Debug, PartialEq, Serialize, Deserialize)] @@ -592,6 +655,19 @@ enum RenameEnumSerializeDeserialize { }, } +#[derive(Debug, PartialEq, Deserialize)] +#[serde(deny_unknown_fields)] +enum AliasEnum { + #[serde(rename = "sailor_moon", alias = "usagi_tsukino")] + SailorMoon { + a: i8, + #[serde(alias = "c")] + b: i8, + #[serde(alias = "e", rename = "f")] + d: i8, + } +} + #[test] fn test_rename_enum() { assert_tokens( @@ -678,6 +754,81 @@ fn test_rename_enum() { Token::StructVariantEnd, ], ); + + assert_de_tokens( + &AliasEnum::SailorMoon { + a: 0, + b: 1, + d: 2, + }, + &[ + Token::StructVariant { + name: "AliasEnum", + variant: "sailor_moon", + len: 3, + }, + Token::Str("a"), + Token::I8(0), + Token::Str("b"), + Token::I8(1), + Token::Str("e"), + Token::I8(2), + Token::StructVariantEnd, + ], + ); + + assert_de_tokens( + &AliasEnum::SailorMoon { + a: 0, + b: 1, + d: 2, + }, + &[ + Token::StructVariant { + name: "AliasEnum", + variant: "usagi_tsukino", + len: 3, + }, + Token::Str("a"), + Token::I8(0), + Token::Str("c"), + Token::I8(1), + Token::Str("f"), + Token::I8(2), + Token::StructVariantEnd, + ], + ); +} + +#[test] +fn test_unknown_field_rename_enum() { + assert_de_tokens_error::( + &[ + Token::StructVariant { + name: "AliasEnum", + variant: "SailorMoon", + len: 3, + }, + ], + "unknown variant `SailorMoon`, expected `sailor_moon`", + ); + + assert_de_tokens_error::( + &[ + Token::StructVariant { + name: "AliasEnum", + variant: "usagi_tsukino", + len: 3, + }, + Token::Str("a"), + Token::I8(0), + Token::Str("c"), + Token::I8(1), + Token::Str("d"), + Token::I8(2), + ], + "unknown field `d`, expected one of `a`, `b`, `f`", + ); } #[derive(Debug, PartialEq, Serialize)] diff --git a/test_suite/tests/ui/conflict/internal-tag-alias.rs b/test_suite/tests/ui/conflict/internal-tag-alias.rs new file mode 100644 index 00000000..81c3a096 --- /dev/null +++ b/test_suite/tests/ui/conflict/internal-tag-alias.rs @@ -0,0 +1,12 @@ +use serde_derive::Serialize; + +#[derive(Serialize)] +#[serde(tag = "conflict")] +enum E { + A { + #[serde(alias = "conflict")] + x: (), + }, +} + +fn main() {} diff --git a/test_suite/tests/ui/conflict/internal-tag-alias.stderr b/test_suite/tests/ui/conflict/internal-tag-alias.stderr new file mode 100644 index 00000000..2fcaa18d --- /dev/null +++ b/test_suite/tests/ui/conflict/internal-tag-alias.stderr @@ -0,0 +1,14 @@ +error: variant field name `conflict` conflicts with internal tag + --> $DIR/internal-tag-alias.rs:4:1 + | +4 | / #[serde(tag = "conflict")] +5 | | enum E { +6 | | A { +7 | | #[serde(alias = "conflict")] +8 | | x: (), +9 | | }, +10 | | } + | |_^ + +error: aborting due to previous error + diff --git a/test_suite/tests/ui/duplicate-attribute/rename-rename-de.rs b/test_suite/tests/ui/duplicate-attribute/rename-rename-de.rs deleted file mode 100644 index 6aeb3301..00000000 --- a/test_suite/tests/ui/duplicate-attribute/rename-rename-de.rs +++ /dev/null @@ -1,10 +0,0 @@ -use serde_derive::Serialize; - -#[derive(Serialize)] -struct S { - #[serde(rename = "x")] - #[serde(rename(deserialize = "y"))] - x: (), -} - -fn main() {} diff --git a/test_suite/tests/ui/duplicate-attribute/rename-rename-de.stderr b/test_suite/tests/ui/duplicate-attribute/rename-rename-de.stderr deleted file mode 100644 index 0824272c..00000000 --- a/test_suite/tests/ui/duplicate-attribute/rename-rename-de.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: duplicate serde attribute `rename` - --> $DIR/rename-rename-de.rs:6:13 - | -6 | #[serde(rename(deserialize = "y"))] - | ^^^^^^ - -error: aborting due to previous error -