Treat unmatched non-exhaustive remote variant as serde(skip)

This commit is contained in:
David Tolnay 2023-08-13 21:14:33 -07:00
parent cb490ec16d
commit 8d3a03288b
No known key found for this signature in database
GPG Key ID: F9BA143B95FF6D82
4 changed files with 30 additions and 1 deletions

View File

@ -1370,3 +1370,16 @@ impl Serialize for AdjacentlyTaggedEnumVariant {
serializer.serialize_unit_variant(self.enum_name, self.variant_index, self.variant_name) serializer.serialize_unit_variant(self.enum_name, self.variant_index, self.variant_name)
} }
} }
// Error when Serialize for a non_exhaustive remote enum encounters a variant
// that is not recognized.
pub struct CannotSerializeVariant<T>(pub T);
impl<T> Display for CannotSerializeVariant<T>
where
T: Debug,
{
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "enum variant cannot be serialized: {:?}", self.0)
}
}

View File

@ -221,6 +221,7 @@ pub struct Container {
is_packed: bool, is_packed: bool,
/// Error message generated when type can't be deserialized /// Error message generated when type can't be deserialized
expecting: Option<String>, expecting: Option<String>,
non_exhaustive: bool,
} }
/// Styles of representing an enum. /// Styles of representing an enum.
@ -306,9 +307,12 @@ impl Container {
let mut variant_identifier = BoolAttr::none(cx, VARIANT_IDENTIFIER); let mut variant_identifier = BoolAttr::none(cx, VARIANT_IDENTIFIER);
let mut serde_path = Attr::none(cx, CRATE); let mut serde_path = Attr::none(cx, CRATE);
let mut expecting = Attr::none(cx, EXPECTING); let mut expecting = Attr::none(cx, EXPECTING);
let mut non_exhaustive = false;
for attr in &item.attrs { for attr in &item.attrs {
if attr.path() != SERDE { if attr.path() != SERDE {
non_exhaustive |=
matches!(&attr.meta, syn::Meta::Path(path) if path == NON_EXHAUSTIVE);
continue; continue;
} }
@ -587,6 +591,7 @@ impl Container {
serde_path: serde_path.get(), serde_path: serde_path.get(),
is_packed, is_packed,
expecting: expecting.get(), expecting: expecting.get(),
non_exhaustive,
} }
} }
@ -672,6 +677,10 @@ impl Container {
pub fn expecting(&self) -> Option<&str> { pub fn expecting(&self) -> Option<&str> {
self.expecting.as_ref().map(String::as_ref) self.expecting.as_ref().map(String::as_ref)
} }
pub fn non_exhaustive(&self) -> bool {
self.non_exhaustive
}
} }
fn decide_tag( fn decide_tag(

View File

@ -19,6 +19,7 @@ pub const FLATTEN: Symbol = Symbol("flatten");
pub const FROM: Symbol = Symbol("from"); pub const FROM: Symbol = Symbol("from");
pub const GETTER: Symbol = Symbol("getter"); pub const GETTER: Symbol = Symbol("getter");
pub const INTO: Symbol = Symbol("into"); pub const INTO: Symbol = Symbol("into");
pub const NON_EXHAUSTIVE: Symbol = Symbol("non_exhaustive");
pub const OTHER: Symbol = Symbol("other"); pub const OTHER: Symbol = Symbol("other");
pub const REMOTE: Symbol = Symbol("remote"); pub const REMOTE: Symbol = Symbol("remote");
pub const RENAME: Symbol = Symbol("rename"); pub const RENAME: Symbol = Symbol("rename");

View File

@ -401,7 +401,7 @@ fn serialize_enum(params: &Parameters, variants: &[Variant], cattrs: &attr::Cont
let self_var = &params.self_var; let self_var = &params.self_var;
let arms: Vec<_> = variants let mut arms: Vec<_> = variants
.iter() .iter()
.enumerate() .enumerate()
.map(|(variant_index, variant)| { .map(|(variant_index, variant)| {
@ -409,6 +409,12 @@ fn serialize_enum(params: &Parameters, variants: &[Variant], cattrs: &attr::Cont
}) })
.collect(); .collect();
if cattrs.non_exhaustive() {
arms.push(quote! {
unrecognized => _serde::__private::Err(_serde::ser::Error::custom(_serde::__private::ser::CannotSerializeVariant(unrecognized))),
});
}
quote_expr! { quote_expr! {
match *#self_var { match *#self_var {
#(#arms)* #(#arms)*