From f9dddaa5fb87a12ae38b3b7190ba09339d11cfe0 Mon Sep 17 00:00:00 2001 From: devnetsec <140857967+devnetsec@users.noreply.github.com> Date: Mon, 12 Aug 2024 18:13:23 -0500 Subject: [PATCH 1/5] Emit an error if a variant of an untagged enum is annotated with #[serde(rename)] or #[serde(alias)]. --- serde_derive/src/internals/ast.rs | 9 ++++++++- serde_derive/src/internals/attr.rs | 6 ++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/serde_derive/src/internals/ast.rs b/serde_derive/src/internals/ast.rs index 4ec709952..4c123b68b 100644 --- a/serde_derive/src/internals/ast.rs +++ b/serde_derive/src/internals/ast.rs @@ -65,8 +65,10 @@ impl<'a> Container<'a> { ) -> Option> { let mut attrs = attr::Container::from_ast(cx, item); + let untagged = attrs.tag() == &attr::TagType::None; + let mut data = match &item.data { - syn::Data::Enum(data) => Data::Enum(enum_from_ast(cx, &data.variants, attrs.default())), + syn::Data::Enum(data) => Data::Enum(enum_from_ast(cx, &data.variants, attrs.default(), untagged)), syn::Data::Struct(data) => { let (style, fields) = struct_from_ast(cx, &data.fields, None, attrs.default()); Data::Struct(style, fields) @@ -141,11 +143,16 @@ fn enum_from_ast<'a>( cx: &Ctxt, variants: &'a Punctuated, container_default: &attr::Default, + container_is_untagged: bool, ) -> Vec> { let variants: Vec = variants .iter() .map(|variant| { let attrs = attr::Variant::from_ast(cx, variant); + + if container_is_untagged && attrs.name().renamed() { + cx.error_spanned_by(&variant.ident, "renaming or adding a alias to a variant of an untagged enum does nothing"); + } let (style, fields) = struct_from_ast(cx, &variant.fields, Some(&attrs), container_default); Variant { diff --git a/serde_derive/src/internals/attr.rs b/serde_derive/src/internals/attr.rs index ba3a2d8d8..03e57f2c9 100644 --- a/serde_derive/src/internals/attr.rs +++ b/serde_derive/src/internals/attr.rs @@ -177,6 +177,11 @@ impl Name { pub fn deserialize_name(&self) -> &str { &self.deserialize } + + /// Return whether the container name was changed for serialization or deserialization. + pub fn renamed(&self) -> bool { + self.serialize_renamed || self.deserialize_renamed || !self.deserialize_aliases.is_empty() + } fn deserialize_aliases(&self) -> &BTreeSet { &self.deserialize_aliases @@ -241,6 +246,7 @@ pub struct Container { } /// Styles of representing an enum. +#[derive(PartialEq)] pub enum TagType { /// The default. /// From 876f5b9b2cd2ba1af5f32ed019ab53e9e378600f Mon Sep 17 00:00:00 2001 From: devnetsec <140857967+devnetsec@users.noreply.github.com> Date: Tue, 13 Aug 2024 21:17:16 -0500 Subject: [PATCH 2/5] Made errors point to attributes instead of variants and added a compile test. --- serde_derive/src/internals/ast.rs | 5 +---- serde_derive/src/internals/attr.rs | 13 +++++++------ .../untagged-and-variant-renamed.rs | 11 +++++++++++ .../untagged-and-variant-renamed.stderr | 11 +++++++++++ 4 files changed, 30 insertions(+), 10 deletions(-) create mode 100644 test_suite/tests/ui/enum-representation/untagged-and-variant-renamed.rs create mode 100644 test_suite/tests/ui/enum-representation/untagged-and-variant-renamed.stderr diff --git a/serde_derive/src/internals/ast.rs b/serde_derive/src/internals/ast.rs index 4c123b68b..4621baf11 100644 --- a/serde_derive/src/internals/ast.rs +++ b/serde_derive/src/internals/ast.rs @@ -148,11 +148,8 @@ fn enum_from_ast<'a>( let variants: Vec = variants .iter() .map(|variant| { - let attrs = attr::Variant::from_ast(cx, variant); + let attrs = attr::Variant::from_ast(cx, variant, container_is_untagged); - if container_is_untagged && attrs.name().renamed() { - cx.error_spanned_by(&variant.ident, "renaming or adding a alias to a variant of an untagged enum does nothing"); - } let (style, fields) = struct_from_ast(cx, &variant.fields, Some(&attrs), container_default); Variant { diff --git a/serde_derive/src/internals/attr.rs b/serde_derive/src/internals/attr.rs index 03e57f2c9..71025289a 100644 --- a/serde_derive/src/internals/attr.rs +++ b/serde_derive/src/internals/attr.rs @@ -178,11 +178,6 @@ impl Name { &self.deserialize } - /// Return whether the container name was changed for serialization or deserialization. - pub fn renamed(&self) -> bool { - self.serialize_renamed || self.deserialize_renamed || !self.deserialize_aliases.is_empty() - } - fn deserialize_aliases(&self) -> &BTreeSet { &self.deserialize_aliases } @@ -843,7 +838,7 @@ struct BorrowAttribute { } impl Variant { - pub fn from_ast(cx: &Ctxt, variant: &syn::Variant) -> Self { + pub fn from_ast(cx: &Ctxt, variant: &syn::Variant, container_is_untagged: bool) -> Self { let mut ser_name = Attr::none(cx, RENAME); let mut de_name = Attr::none(cx, RENAME); let mut de_aliases = VecAttr::none(cx, RENAME); @@ -872,6 +867,9 @@ impl Variant { if let Err(err) = attr.parse_nested_meta(|meta| { if meta.path == RENAME { + if container_is_untagged { + cx.error_spanned_by(&attr, "renaming a variant of an untagged enum does nothing"); + } // #[serde(rename = "foo")] // #[serde(rename(serialize = "foo", deserialize = "bar"))] let (ser, de) = get_multiple_renames(cx, &meta)?; @@ -882,6 +880,9 @@ impl Variant { } } else if meta.path == ALIAS { // #[serde(alias = "foo")] + if container_is_untagged { + cx.error_spanned_by(&attr, "adding a alias to a variant of an untagged enum does nothing"); + } if let Some(s) = get_lit_str(cx, ALIAS, &meta)? { de_aliases.insert(&meta.path, s.value()); } 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..24fa5c931 --- /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)] +enum E { + #[serde(alias = "different-name")] + A(u8), + #[serde(rename = "different-name")] + 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..741988412 --- /dev/null +++ b/test_suite/tests/ui/enum-representation/untagged-and-variant-renamed.stderr @@ -0,0 +1,11 @@ +error: adding a alias to a variant of an untagged enum does nothing + --> tests/ui/enum-representation/untagged-and-variant-renamed.rs:6:2 + | +6 | #[serde(alias = "different-name")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: renaming a variant of an untagged enum does nothing + --> tests/ui/enum-representation/untagged-and-variant-renamed.rs:8:2 + | +8 | #[serde(rename = "different-name")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 6d725642135e5d34fcde8cda00324b71d66b9e52 Mon Sep 17 00:00:00 2001 From: devnetsec <140857967+devnetsec@users.noreply.github.com> Date: Wed, 14 Aug 2024 13:11:40 -0500 Subject: [PATCH 3/5] Made errors point to attribute names only instead of the whole line. --- serde_derive/src/internals/attr.rs | 8 ++++---- .../untagged-and-variant-renamed.rs | 4 ++-- .../untagged-and-variant-renamed.stderr | 12 ++++++------ 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/serde_derive/src/internals/attr.rs b/serde_derive/src/internals/attr.rs index 71025289a..e51b4eee7 100644 --- a/serde_derive/src/internals/attr.rs +++ b/serde_derive/src/internals/attr.rs @@ -325,7 +325,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 |= @@ -467,7 +467,7 @@ impl Container { // #[serde(untagged)] match item.data { syn::Data::Enum(_) => { - untagged.set_true(&meta.path); + untagged.set_true(&meta.path); } syn::Data::Struct(_) => { let msg = "#[serde(untagged)] can only be used on enums"; @@ -868,7 +868,7 @@ impl Variant { if let Err(err) = attr.parse_nested_meta(|meta| { if meta.path == RENAME { if container_is_untagged { - cx.error_spanned_by(&attr, "renaming a variant of an untagged enum does nothing"); + cx.error_spanned_by(&meta.path, "renaming a variant of an untagged enum does nothing"); } // #[serde(rename = "foo")] // #[serde(rename(serialize = "foo", deserialize = "bar"))] @@ -881,7 +881,7 @@ impl Variant { } else if meta.path == ALIAS { // #[serde(alias = "foo")] if container_is_untagged { - cx.error_spanned_by(&attr, "adding a alias to a variant of an untagged enum does nothing"); + cx.error_spanned_by(&meta.path, "adding a alias to a variant of an untagged enum does nothing"); } if let Some(s) = get_lit_str(cx, ALIAS, &meta)? { de_aliases.insert(&meta.path, s.value()); 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 index 24fa5c931..1dd026f19 100644 --- a/test_suite/tests/ui/enum-representation/untagged-and-variant-renamed.rs +++ b/test_suite/tests/ui/enum-representation/untagged-and-variant-renamed.rs @@ -3,9 +3,9 @@ use serde_derive::{Serialize, Deserialize}; #[derive(Serialize, Deserialize)] #[serde(untagged)] enum E { - #[serde(alias = "different-name")] + #[serde(alias = "foo")] A(u8), - #[serde(rename = "different-name")] + #[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 index 741988412..059c04908 100644 --- a/test_suite/tests/ui/enum-representation/untagged-and-variant-renamed.stderr +++ b/test_suite/tests/ui/enum-representation/untagged-and-variant-renamed.stderr @@ -1,11 +1,11 @@ error: adding a alias to a variant of an untagged enum does nothing - --> tests/ui/enum-representation/untagged-and-variant-renamed.rs:6:2 + --> tests/ui/enum-representation/untagged-and-variant-renamed.rs:6:10 | -6 | #[serde(alias = "different-name")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +6 | #[serde(alias = "foo")] + | ^^^^^ error: renaming a variant of an untagged enum does nothing - --> tests/ui/enum-representation/untagged-and-variant-renamed.rs:8:2 + --> tests/ui/enum-representation/untagged-and-variant-renamed.rs:8:10 | -8 | #[serde(rename = "different-name")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +8 | #[serde(rename = "bar")] + | ^^^^^^ From 453d1dce57e78e9fb567577b2abdb0efd6050d28 Mon Sep 17 00:00:00 2001 From: devnetsec <140857967+devnetsec@users.noreply.github.com> Date: Sat, 17 Aug 2024 17:18:46 -0500 Subject: [PATCH 4/5] Add support for #[serde(rename_all)] as well and fix indentation. --- serde_derive/src/internals/ast.rs | 2 +- serde_derive/src/internals/attr.rs | 38 ++++++++++++++----- .../untagged-and-variant-renamed.rs | 10 ++--- .../untagged-and-variant-renamed.stderr | 10 ++++- 4 files changed, 43 insertions(+), 17 deletions(-) diff --git a/serde_derive/src/internals/ast.rs b/serde_derive/src/internals/ast.rs index 4621baf11..1d99bbac6 100644 --- a/serde_derive/src/internals/ast.rs +++ b/serde_derive/src/internals/ast.rs @@ -65,7 +65,7 @@ impl<'a> Container<'a> { ) -> Option> { let mut attrs = attr::Container::from_ast(cx, item); - let untagged = attrs.tag() == &attr::TagType::None; + let untagged = attrs.tag() == &attr::TagType::None; let mut data = match &item.data { syn::Data::Enum(data) => Data::Enum(enum_from_ast(cx, &data.variants, attrs.default(), untagged)), diff --git a/serde_derive/src/internals/attr.rs b/serde_derive/src/internals/attr.rs index e51b4eee7..5ad497dc4 100644 --- a/serde_derive/src/internals/attr.rs +++ b/serde_derive/src/internals/attr.rs @@ -467,7 +467,7 @@ impl Container { // #[serde(untagged)] match item.data { syn::Data::Enum(_) => { - untagged.set_true(&meta.path); + untagged.set_true(&meta.path); } syn::Data::Struct(_) => { let msg = "#[serde(untagged)] can only be used on enums"; @@ -583,14 +583,34 @@ impl Container { } } + let [serialize, deserialize]: [RenameRule; 2]; + if untagged.get() { + if let Some((tokens, rule)) = rename_all_ser_rule.get_with_tokens() { + serialize = rule; + deserialize = rename_all_de_rule.get().unwrap_or(RenameRule::None); + cx.error_spanned_by(&tokens, "renaming a variant of an untagged enum does nothing"); + } else if let Some((tokens, rule)) = rename_all_de_rule.get_with_tokens() { + serialize = rule; + deserialize = RenameRule::None; + cx.error_spanned_by(&tokens, "renaming a variant of an untagged enum does nothing"); + } else { + serialize = RenameRule::None; + deserialize = RenameRule::None; + } + + } else { + serialize = rename_all_ser_rule.get().unwrap_or(RenameRule::None); + deserialize = rename_all_de_rule.get().unwrap_or(RenameRule::None); + } + Container { 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), rename_all_rules: RenameAllRules { - serialize: rename_all_ser_rule.get().unwrap_or(RenameRule::None), - deserialize: rename_all_de_rule.get().unwrap_or(RenameRule::None), + serialize, + deserialize, }, rename_all_fields_rules: RenameAllRules { serialize: rename_all_fields_ser_rule.get().unwrap_or(RenameRule::None), @@ -867,11 +887,11 @@ impl Variant { if let Err(err) = attr.parse_nested_meta(|meta| { if meta.path == RENAME { - if container_is_untagged { - cx.error_spanned_by(&meta.path, "renaming a variant of an untagged enum does nothing"); - } // #[serde(rename = "foo")] // #[serde(rename(serialize = "foo", deserialize = "bar"))] + if container_is_untagged { + cx.error_spanned_by(&meta.path, "renaming a variant of an untagged enum does nothing"); + } let (ser, de) = get_multiple_renames(cx, &meta)?; ser_name.set_opt(&meta.path, ser.as_ref().map(syn::LitStr::value)); for de_value in de { @@ -880,9 +900,9 @@ impl Variant { } } else if meta.path == ALIAS { // #[serde(alias = "foo")] - if container_is_untagged { - cx.error_spanned_by(&meta.path, "adding a alias to a variant of an untagged enum does nothing"); - } + if container_is_untagged { + cx.error_spanned_by(&meta.path, "adding a alias to a variant of an untagged enum does nothing"); + } if let Some(s) = get_lit_str(cx, ALIAS, &meta)? { de_aliases.insert(&meta.path, s.value()); } 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 index 1dd026f19..f88daebd6 100644 --- a/test_suite/tests/ui/enum-representation/untagged-and-variant-renamed.rs +++ b/test_suite/tests/ui/enum-representation/untagged-and-variant-renamed.rs @@ -1,11 +1,11 @@ use serde_derive::{Serialize, Deserialize}; #[derive(Serialize, Deserialize)] -#[serde(untagged)] +#[serde(untagged, rename_all = "lowercase")] enum E { - #[serde(alias = "foo")] - A(u8), - #[serde(rename = "bar")] - B(String), + #[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 index 059c04908..ec5e68df3 100644 --- a/test_suite/tests/ui/enum-representation/untagged-and-variant-renamed.stderr +++ b/test_suite/tests/ui/enum-representation/untagged-and-variant-renamed.stderr @@ -1,11 +1,17 @@ +error: renaming a variant of an untagged enum does nothing + --> tests/ui/enum-representation/untagged-and-variant-renamed.rs:4:19 + | +4 | #[serde(untagged, rename_all = "lowercase")] + | ^^^^^^^^^^ + error: adding a alias to a variant of an untagged enum does nothing - --> tests/ui/enum-representation/untagged-and-variant-renamed.rs:6:10 + --> tests/ui/enum-representation/untagged-and-variant-renamed.rs:6:13 | 6 | #[serde(alias = "foo")] | ^^^^^ error: renaming a variant of an untagged enum does nothing - --> tests/ui/enum-representation/untagged-and-variant-renamed.rs:8:10 + --> tests/ui/enum-representation/untagged-and-variant-renamed.rs:8:13 | 8 | #[serde(rename = "bar")] | ^^^^^^ From 5e629d9943b5b9a8c2bde3baff773137f9092ac4 Mon Sep 17 00:00:00 2001 From: devnetsec <140857967+devnetsec@users.noreply.github.com> Date: Thu, 22 Aug 2024 17:03:30 -0500 Subject: [PATCH 5/5] Move checks to check.rs --- serde_derive/src/internals/ast.rs | 10 ++---- serde_derive/src/internals/attr.rs | 36 ++++--------------- serde_derive/src/internals/check.rs | 34 ++++++++++++++++++ .../untagged-and-variant-renamed.stderr | 34 +++++++++++------- 4 files changed, 65 insertions(+), 49 deletions(-) diff --git a/serde_derive/src/internals/ast.rs b/serde_derive/src/internals/ast.rs index 1d99bbac6..737e32702 100644 --- a/serde_derive/src/internals/ast.rs +++ b/serde_derive/src/internals/ast.rs @@ -64,11 +64,9 @@ impl<'a> Container<'a> { derive: Derive, ) -> Option> { let mut attrs = attr::Container::from_ast(cx, item); - - let untagged = attrs.tag() == &attr::TagType::None; - + let mut data = match &item.data { - syn::Data::Enum(data) => Data::Enum(enum_from_ast(cx, &data.variants, attrs.default(), untagged)), + syn::Data::Enum(data) => Data::Enum(enum_from_ast(cx, &data.variants, attrs.default())), syn::Data::Struct(data) => { let (style, fields) = struct_from_ast(cx, &data.fields, None, attrs.default()); Data::Struct(style, fields) @@ -143,13 +141,11 @@ fn enum_from_ast<'a>( cx: &Ctxt, variants: &'a Punctuated, container_default: &attr::Default, - container_is_untagged: bool, ) -> Vec> { let variants: Vec = variants .iter() .map(|variant| { - let attrs = attr::Variant::from_ast(cx, variant, container_is_untagged); - + let attrs = attr::Variant::from_ast(cx, variant); let (style, fields) = struct_from_ast(cx, &variant.fields, Some(&attrs), container_default); Variant { diff --git a/serde_derive/src/internals/attr.rs b/serde_derive/src/internals/attr.rs index 5ad497dc4..7310debff 100644 --- a/serde_derive/src/internals/attr.rs +++ b/serde_derive/src/internals/attr.rs @@ -181,6 +181,10 @@ impl Name { fn deserialize_aliases(&self) -> &BTreeSet { &self.deserialize_aliases } + + pub fn renamed(&self) -> bool { + self.serialize_renamed || self.deserialize_renamed + } } #[derive(Copy, Clone)] @@ -583,34 +587,14 @@ impl Container { } } - let [serialize, deserialize]: [RenameRule; 2]; - if untagged.get() { - if let Some((tokens, rule)) = rename_all_ser_rule.get_with_tokens() { - serialize = rule; - deserialize = rename_all_de_rule.get().unwrap_or(RenameRule::None); - cx.error_spanned_by(&tokens, "renaming a variant of an untagged enum does nothing"); - } else if let Some((tokens, rule)) = rename_all_de_rule.get_with_tokens() { - serialize = rule; - deserialize = RenameRule::None; - cx.error_spanned_by(&tokens, "renaming a variant of an untagged enum does nothing"); - } else { - serialize = RenameRule::None; - deserialize = RenameRule::None; - } - - } else { - serialize = rename_all_ser_rule.get().unwrap_or(RenameRule::None); - deserialize = rename_all_de_rule.get().unwrap_or(RenameRule::None); - } - Container { 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), rename_all_rules: RenameAllRules { - serialize, - deserialize, + serialize: rename_all_ser_rule.get().unwrap_or(RenameRule::None), + deserialize: rename_all_de_rule.get().unwrap_or(RenameRule::None), }, rename_all_fields_rules: RenameAllRules { serialize: rename_all_fields_ser_rule.get().unwrap_or(RenameRule::None), @@ -858,7 +842,7 @@ struct BorrowAttribute { } impl Variant { - pub fn from_ast(cx: &Ctxt, variant: &syn::Variant, container_is_untagged: bool) -> Self { + 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_aliases = VecAttr::none(cx, RENAME); @@ -889,9 +873,6 @@ impl Variant { if meta.path == RENAME { // #[serde(rename = "foo")] // #[serde(rename(serialize = "foo", deserialize = "bar"))] - if container_is_untagged { - cx.error_spanned_by(&meta.path, "renaming a variant of an untagged enum does nothing"); - } let (ser, de) = get_multiple_renames(cx, &meta)?; ser_name.set_opt(&meta.path, ser.as_ref().map(syn::LitStr::value)); for de_value in de { @@ -900,9 +881,6 @@ impl Variant { } } else if meta.path == ALIAS { // #[serde(alias = "foo")] - if container_is_untagged { - cx.error_spanned_by(&meta.path, "adding a alias to a variant of an untagged enum does nothing"); - } if let Some(s) = get_lit_str(cx, ALIAS, &meta)? { de_aliases.insert(&meta.path, s.value()); } 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.stderr b/test_suite/tests/ui/enum-representation/untagged-and-variant-renamed.stderr index ec5e68df3..c619d7cfb 100644 --- a/test_suite/tests/ui/enum-representation/untagged-and-variant-renamed.stderr +++ b/test_suite/tests/ui/enum-representation/untagged-and-variant-renamed.stderr @@ -1,17 +1,25 @@ -error: renaming a variant of an untagged enum does nothing - --> tests/ui/enum-representation/untagged-and-variant-renamed.rs:4:19 - | -4 | #[serde(untagged, rename_all = "lowercase")] - | ^^^^^^^^^^ +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: adding a alias to a variant of an untagged enum does nothing - --> tests/ui/enum-representation/untagged-and-variant-renamed.rs:6:13 +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")] - | ^^^^^ +6 | / #[serde(alias = "foo")] +7 | | A(u8), + | |_________^ -error: renaming a variant of an untagged enum does nothing - --> tests/ui/enum-representation/untagged-and-variant-renamed.rs:8:13 +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")] - | ^^^^^^ +8 | / #[serde(rename = "bar")] +9 | | B(String), + | |_____________^