diff --git a/serde_derive/src/internals/ast.rs b/serde_derive/src/internals/ast.rs index 4ec709952..737e32702 100644 --- a/serde_derive/src/internals/ast.rs +++ b/serde_derive/src/internals/ast.rs @@ -64,7 +64,7 @@ impl<'a> Container<'a> { derive: Derive, ) -> Option> { let mut attrs = attr::Container::from_ast(cx, item); - + let mut data = match &item.data { syn::Data::Enum(data) => Data::Enum(enum_from_ast(cx, &data.variants, attrs.default())), syn::Data::Struct(data) => { diff --git a/serde_derive/src/internals/attr.rs b/serde_derive/src/internals/attr.rs index ba3a2d8d8..7310debff 100644 --- a/serde_derive/src/internals/attr.rs +++ b/serde_derive/src/internals/attr.rs @@ -177,10 +177,14 @@ impl Name { pub fn deserialize_name(&self) -> &str { &self.deserialize } - + fn deserialize_aliases(&self) -> &BTreeSet { &self.deserialize_aliases } + + pub fn renamed(&self) -> bool { + self.serialize_renamed || self.deserialize_renamed + } } #[derive(Copy, Clone)] @@ -241,6 +245,7 @@ pub struct Container { } /// Styles of representing an enum. +#[derive(PartialEq)] pub enum TagType { /// The default. /// @@ -324,7 +329,7 @@ impl Container { let mut serde_path = Attr::none(cx, CRATE); let mut expecting = Attr::none(cx, EXPECTING); let mut non_exhaustive = false; - + for attr in &item.attrs { if attr.path() != SERDE { non_exhaustive |= diff --git a/serde_derive/src/internals/check.rs b/serde_derive/src/internals/check.rs index 52b0f379f..6f6b1c989 100644 --- a/serde_derive/src/internals/check.rs +++ b/serde_derive/src/internals/check.rs @@ -1,5 +1,6 @@ use crate::internals::ast::{Container, Data, Field, Style}; use crate::internals::attr::{Default, Identifier, TagType}; +use crate::internals::case::RenameRule; use crate::internals::{ungroup, Ctxt, Derive}; use syn::{Member, Type}; @@ -16,6 +17,7 @@ pub fn check(cx: &Ctxt, cont: &mut Container, derive: Derive) { check_adjacent_tag_conflict(cx, cont); check_transparent(cx, cont, derive); check_from_and_try_from(cx, cont); + check_untagged_enum(cx, cont); } // If some field of a tuple struct is marked #[serde(default)] then all fields @@ -475,3 +477,35 @@ fn check_from_and_try_from(cx: &Ctxt, cont: &mut Container) { ); } } + +fn check_untagged_enum(cx: &Ctxt, cont: &mut Container) { + let variants = match &cont.data { + Data::Enum(variants) => variants, + Data::Struct(_, _) => return, + }; + if cont.attrs.tag() == &TagType::None { + let rename_all_rules = &cont.attrs.rename_all_rules(); + if rename_all_rules.serialize != RenameRule::None || rename_all_rules.deserialize != RenameRule::None { + cx.error_spanned_by( + cont.original, + "#[serde(rename_all = \"...\")] does nothing when used with #[serde(untagged)]", + ); + } + + for variant in variants { + if variant.attrs.aliases().len() > 1 { + cx.error_spanned_by( + variant.original, + "#[serde(alias = \"...\")] does nothing when used on a variant of an untagged enum", + ); + } + + if variant.attrs.name().renamed() { + cx.error_spanned_by( + variant.original, + "#[serde(rename = \"...\")] does nothing when used on a variant of an untagged enum", + ); + } + } + } +} diff --git a/test_suite/tests/ui/enum-representation/untagged-and-variant-renamed.rs b/test_suite/tests/ui/enum-representation/untagged-and-variant-renamed.rs new file mode 100644 index 000000000..f88daebd6 --- /dev/null +++ b/test_suite/tests/ui/enum-representation/untagged-and-variant-renamed.rs @@ -0,0 +1,11 @@ +use serde_derive::{Serialize, Deserialize}; + +#[derive(Serialize, Deserialize)] +#[serde(untagged, rename_all = "lowercase")] +enum E { + #[serde(alias = "foo")] + A(u8), + #[serde(rename = "bar")] + B(String), +} +fn main() {} diff --git a/test_suite/tests/ui/enum-representation/untagged-and-variant-renamed.stderr b/test_suite/tests/ui/enum-representation/untagged-and-variant-renamed.stderr new file mode 100644 index 000000000..c619d7cfb --- /dev/null +++ b/test_suite/tests/ui/enum-representation/untagged-and-variant-renamed.stderr @@ -0,0 +1,25 @@ +error: #[serde(rename_all = "...")] does nothing when used with #[serde(untagged)] + --> tests/ui/enum-representation/untagged-and-variant-renamed.rs:4:1 + | +4 | / #[serde(untagged, rename_all = "lowercase")] +5 | | enum E { +6 | | #[serde(alias = "foo")] +7 | | A(u8), +8 | | #[serde(rename = "bar")] +9 | | B(String), +10 | | } + | |_^ + +error: #[serde(alias = "...")] does nothing when used on a variant of an untagged enum + --> tests/ui/enum-representation/untagged-and-variant-renamed.rs:6:5 + | +6 | / #[serde(alias = "foo")] +7 | | A(u8), + | |_________^ + +error: #[serde(rename = "...")] does nothing when used on a variant of an untagged enum + --> tests/ui/enum-representation/untagged-and-variant-renamed.rs:8:5 + | +8 | / #[serde(rename = "bar")] +9 | | B(String), + | |_____________^