Fixed derive impl on an empty enum (#462)

* Fixed derive impl on an empty enum

* Added DecodeError::EmptyEnum in case a user tries to decode an empty enum
This commit is contained in:
Trangar 2022-01-08 12:39:03 +01:00 committed by GitHub
parent 030905e7d5
commit 99707d0d0b
3 changed files with 131 additions and 90 deletions

View File

@ -35,6 +35,9 @@ impl DeriveEnum {
fn_body.ident_str("match");
fn_body.ident_str("self");
fn_body.group(Delimiter::Brace, |match_body| {
if self.variants.is_empty() {
self.encode_empty_enum_case(match_body)?;
}
for (variant_index, variant) in self.iter_fields() {
// Self::Variant
match_body.ident_str("Self");
@ -101,18 +104,24 @@ impl DeriveEnum {
?;
}
}
body.push_parsed("Ok(())")?;
Ok(())
})?;
match_body.punct(',');
}
Ok(())
})?;
fn_body.push_parsed("Ok(())")?;
Ok(())
})?;
Ok(())
}
/// If we're encoding an empty enum, we need to add an empty case in the form of:
/// `_ => core::unreachable!(),`
fn encode_empty_enum_case(&self, builder: &mut StreamBuilder) -> Result {
builder.push_parsed("_ => core::unreachable!()").map(|_| ())
}
/// Build the catch-all case for an int-to-enum decode implementation
fn invalid_variant_case(&self, enum_name: &str, result: &mut StreamBuilder) -> Result {
// we'll be generating:
@ -195,6 +204,9 @@ impl DeriveEnum {
.with_arg("mut decoder", "D")
.with_return_type("core::result::Result<Self, bincode::error::DecodeError>")
.body(|fn_builder| {
if self.variants.is_empty() {
fn_builder.push_parsed("core::result::Result::Err(bincode::error::DecodeError::EmptyEnum { type_name: core::any::type_name::<Self>() })")?;
} else {
fn_builder
.push_parsed(
"let variant_index = <u32 as bincode::Decode>::decode(&mut decoder)?;",
@ -246,6 +258,7 @@ impl DeriveEnum {
// invalid idx
self.invalid_variant_case(&enum_name, variant_case)
})?;
}
Ok(())
})?;
Ok(())
@ -267,6 +280,9 @@ impl DeriveEnum {
.with_arg("mut decoder", "D")
.with_return_type("core::result::Result<Self, bincode::error::DecodeError>")
.body(|fn_builder| {
if self.variants.is_empty() {
fn_builder.push_parsed("core::result::Result::Err(bincode::error::DecodeError::EmptyEnum { type_name: core::any::type_name::<Self>() })")?;
} else {
fn_builder
.push_parsed("let variant_index = <u32 as bincode::Decode>::decode(&mut decoder)?;")?;
fn_builder.push_parsed("match variant_index")?;
@ -315,6 +331,7 @@ impl DeriveEnum {
// invalid idx
self.invalid_variant_case(&enum_name, variant_case)
})?;
}
Ok(())
})?;
Ok(())

View File

@ -116,6 +116,12 @@ pub enum DecodeError {
found: usize,
},
/// Tried to decode an enum with no variants
EmptyEnum {
/// The type that was being decoded
type_name: &'static str,
},
/// The decoder tried to decode a `CStr` or `CString`, but the incoming data contained a 0 byte
#[cfg(feature = "std")]
CStrNulError {

View File

@ -251,3 +251,21 @@ fn test_macro_newtype() {
assert_eq!(len, newtype_len);
}
}
#[derive(bincode::Encode, bincode::Decode, Debug)]
pub enum EmptyEnum {}
#[derive(bincode::Encode, bincode::BorrowDecode, Debug)]
pub enum BorrowedEmptyEnum {}
#[test]
fn test_empty_enum_decode() {
let err =
bincode::decode_from_slice::<EmptyEnum, _>(&[], Configuration::standard()).unwrap_err();
assert_eq!(
err,
bincode::error::DecodeError::EmptyEnum {
type_name: "derive::EmptyEnum"
}
);
}