Skip to content

Commit 8707d4d

Browse files
committed
Allow only one unit variant in untagged enums, because we cannot distinguish between them when deserialize
If you really need several unit variants, mark all of them except one with `#[serde(skip_deserializing)]`
1 parent da3998a commit 8707d4d

File tree

3 files changed

+75
-0
lines changed

3 files changed

+75
-0
lines changed

serde_derive/src/internals/check.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ pub fn check(cx: &Ctxt, cont: &mut Container, derive: Derive) {
1616
check_adjacent_tag_conflict(cx, cont);
1717
check_transparent(cx, cont, derive);
1818
check_from_and_try_from(cx, cont);
19+
check_enum_untagged(cx, cont, derive);
1920
}
2021

2122
// If some field of a tuple struct is marked #[serde(default)] then all fields
@@ -475,3 +476,36 @@ fn check_from_and_try_from(cx: &Ctxt, cont: &mut Container) {
475476
);
476477
}
477478
}
479+
480+
/// Checks that untagged enum has only one unit variant, because we cannot distinguish between
481+
/// different variants when deserializing
482+
fn check_enum_untagged(cx: &Ctxt, cont: &mut Container, derive: Derive) {
483+
// We allow serialization of enums with multiple units, because new units could
484+
// be added to maintain backward compatibility
485+
if let Derive::Serialize = derive {
486+
return;
487+
}
488+
let variants = match &cont.data {
489+
Data::Enum(variants) => variants,
490+
Data::Struct(_, _) => return,
491+
};
492+
if !matches!(cont.attrs.tag(), TagType::None) {
493+
return;
494+
}
495+
496+
let mut unit = None;
497+
for variant in variants {
498+
if variant.attrs.skip_deserializing() {
499+
continue;
500+
}
501+
if let Style::Unit = variant.style {
502+
if unit.is_some() {
503+
cx.error_spanned_by(
504+
variant.original,
505+
"untagged enums can contain only one unit variant. Use #[serde(skip_deserializing)] if you really want several unit variants",
506+
);
507+
}
508+
unit = Some(variant);
509+
}
510+
}
511+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
use serde_derive::{Deserialize, Serialize};
2+
3+
#[derive(Deserialize)]
4+
#[serde(untagged)]
5+
enum E1 {
6+
Unit1,
7+
Tuple1(usize),
8+
Unit2,
9+
Struct {},
10+
#[serde(skip_serializing)]
11+
Unit3,
12+
#[serde(skip_deserializing)]
13+
Unit4,
14+
}
15+
16+
#[derive(Serialize)]
17+
#[serde(untagged)]
18+
enum E2 {
19+
Unit1,
20+
Tuple1(usize),
21+
Unit2,
22+
Struct {},
23+
#[serde(skip_serializing)]
24+
Unit3,
25+
#[serde(skip_deserializing)]
26+
Unit4,
27+
}
28+
29+
fn main() {}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error: untagged enums can contain only one unit variant. Use #[serde(skip_deserializing)] if you really want several unit variants
2+
--> tests/ui/enum-representation/untagged-with-muliple-units.rs:8:5
3+
|
4+
8 | Unit2,
5+
| ^^^^^
6+
7+
error: untagged enums can contain only one unit variant. Use #[serde(skip_deserializing)] if you really want several unit variants
8+
--> tests/ui/enum-representation/untagged-with-muliple-units.rs:10:5
9+
|
10+
10 | / #[serde(skip_serializing)]
11+
11 | | Unit3,
12+
| |_________^

0 commit comments

Comments
 (0)