Skip to content
This repository was archived by the owner on Aug 15, 2025. It is now read-only.

Commit 53acac9

Browse files
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
1 parent db398ec commit 53acac9

File tree

3 files changed

+131
-90
lines changed

3 files changed

+131
-90
lines changed

derive/src/derive_enum.rs

Lines changed: 107 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ impl DeriveEnum {
3535
fn_body.ident_str("match");
3636
fn_body.ident_str("self");
3737
fn_body.group(Delimiter::Brace, |match_body| {
38+
if self.variants.is_empty() {
39+
self.encode_empty_enum_case(match_body)?;
40+
}
3841
for (variant_index, variant) in self.iter_fields() {
3942
// Self::Variant
4043
match_body.ident_str("Self");
@@ -101,18 +104,24 @@ impl DeriveEnum {
101104
?;
102105
}
103106
}
107+
body.push_parsed("Ok(())")?;
104108
Ok(())
105109
})?;
106110
match_body.punct(',');
107111
}
108112
Ok(())
109113
})?;
110-
fn_body.push_parsed("Ok(())")?;
111114
Ok(())
112115
})?;
113116
Ok(())
114117
}
115118

119+
/// If we're encoding an empty enum, we need to add an empty case in the form of:
120+
/// `_ => core::unreachable!(),`
121+
fn encode_empty_enum_case(&self, builder: &mut StreamBuilder) -> Result {
122+
builder.push_parsed("_ => core::unreachable!()").map(|_| ())
123+
}
124+
116125
/// Build the catch-all case for an int-to-enum decode implementation
117126
fn invalid_variant_case(&self, enum_name: &str, result: &mut StreamBuilder) -> Result {
118127
// we'll be generating:
@@ -195,57 +204,61 @@ impl DeriveEnum {
195204
.with_arg("mut decoder", "D")
196205
.with_return_type("core::result::Result<Self, bincode::error::DecodeError>")
197206
.body(|fn_builder| {
198-
fn_builder
199-
.push_parsed(
200-
"let variant_index = <u32 as bincode::Decode>::decode(&mut decoder)?;",
201-
)?;
202-
fn_builder.push_parsed("match variant_index")?;
203-
fn_builder.group(Delimiter::Brace, |variant_case| {
204-
for (mut variant_index, variant) in self.iter_fields() {
205-
// idx => Ok(..)
206-
if variant_index.len() > 1 {
207-
variant_case.push_parsed("x if x == ")?;
208-
variant_case.extend(variant_index);
209-
} else {
210-
variant_case.push(variant_index.remove(0));
211-
}
212-
variant_case.puncts("=>");
213-
variant_case.ident_str("Ok");
214-
variant_case.group(Delimiter::Parenthesis, |variant_case_body| {
215-
// Self::Variant { }
216-
// Self::Variant { 0: ..., 1: ... 2: ... },
217-
// Self::Variant { a: ..., b: ... c: ... },
218-
variant_case_body.ident_str("Self");
219-
variant_case_body.puncts("::");
220-
variant_case_body.ident(variant.name.clone());
207+
if self.variants.is_empty() {
208+
fn_builder.push_parsed("core::result::Result::Err(bincode::error::DecodeError::EmptyEnum { type_name: core::any::type_name::<Self>() })")?;
209+
} else {
210+
fn_builder
211+
.push_parsed(
212+
"let variant_index = <u32 as bincode::Decode>::decode(&mut decoder)?;",
213+
)?;
214+
fn_builder.push_parsed("match variant_index")?;
215+
fn_builder.group(Delimiter::Brace, |variant_case| {
216+
for (mut variant_index, variant) in self.iter_fields() {
217+
// idx => Ok(..)
218+
if variant_index.len() > 1 {
219+
variant_case.push_parsed("x if x == ")?;
220+
variant_case.extend(variant_index);
221+
} else {
222+
variant_case.push(variant_index.remove(0));
223+
}
224+
variant_case.puncts("=>");
225+
variant_case.ident_str("Ok");
226+
variant_case.group(Delimiter::Parenthesis, |variant_case_body| {
227+
// Self::Variant { }
228+
// Self::Variant { 0: ..., 1: ... 2: ... },
229+
// Self::Variant { a: ..., b: ... c: ... },
230+
variant_case_body.ident_str("Self");
231+
variant_case_body.puncts("::");
232+
variant_case_body.ident(variant.name.clone());
221233

222-
variant_case_body.group(Delimiter::Brace, |variant_body| {
223-
let is_tuple = matches!(variant.fields, Fields::Tuple(_));
224-
for (idx, field) in variant.fields.names().into_iter().enumerate() {
225-
if is_tuple {
226-
variant_body.lit_usize(idx);
227-
} else {
228-
variant_body.ident(field.unwrap_ident().clone());
234+
variant_case_body.group(Delimiter::Brace, |variant_body| {
235+
let is_tuple = matches!(variant.fields, Fields::Tuple(_));
236+
for (idx, field) in variant.fields.names().into_iter().enumerate() {
237+
if is_tuple {
238+
variant_body.lit_usize(idx);
239+
} else {
240+
variant_body.ident(field.unwrap_ident().clone());
241+
}
242+
variant_body.punct(':');
243+
if field.attributes().has_attribute(FieldAttribute::WithSerde)? {
244+
variant_body
245+
.push_parsed("<bincode::serde::Compat<_> as bincode::Decode>::decode(&mut decoder)?.0,")?;
246+
} else {
247+
variant_body
248+
.push_parsed("bincode::Decode::decode(&mut decoder)?,")?;
249+
}
229250
}
230-
variant_body.punct(':');
231-
if field.attributes().has_attribute(FieldAttribute::WithSerde)? {
232-
variant_body
233-
.push_parsed("<bincode::serde::Compat<_> as bincode::Decode>::decode(&mut decoder)?.0,")?;
234-
} else {
235-
variant_body
236-
.push_parsed("bincode::Decode::decode(&mut decoder)?,")?;
237-
}
238-
}
251+
Ok(())
252+
})?;
239253
Ok(())
240254
})?;
241-
Ok(())
242-
})?;
243-
variant_case.punct(',');
244-
}
255+
variant_case.punct(',');
256+
}
245257

246-
// invalid idx
247-
self.invalid_variant_case(&enum_name, variant_case)
248-
})?;
258+
// invalid idx
259+
self.invalid_variant_case(&enum_name, variant_case)
260+
})?;
261+
}
249262
Ok(())
250263
})?;
251264
Ok(())
@@ -267,54 +280,58 @@ impl DeriveEnum {
267280
.with_arg("mut decoder", "D")
268281
.with_return_type("core::result::Result<Self, bincode::error::DecodeError>")
269282
.body(|fn_builder| {
270-
fn_builder
271-
.push_parsed("let variant_index = <u32 as bincode::Decode>::decode(&mut decoder)?;")?;
272-
fn_builder.push_parsed("match variant_index")?;
273-
fn_builder.group(Delimiter::Brace, |variant_case| {
274-
for (mut variant_index, variant) in self.iter_fields() {
275-
// idx => Ok(..)
276-
if variant_index.len() > 1 {
277-
variant_case.push_parsed("x if x == ")?;
278-
variant_case.extend(variant_index);
279-
} else {
280-
variant_case.push(variant_index.remove(0));
281-
}
282-
variant_case.puncts("=>");
283-
variant_case.ident_str("Ok");
284-
variant_case.group(Delimiter::Parenthesis, |variant_case_body| {
285-
// Self::Variant { }
286-
// Self::Variant { 0: ..., 1: ... 2: ... },
287-
// Self::Variant { a: ..., b: ... c: ... },
288-
variant_case_body.ident_str("Self");
289-
variant_case_body.puncts("::");
290-
variant_case_body.ident(variant.name.clone());
283+
if self.variants.is_empty() {
284+
fn_builder.push_parsed("core::result::Result::Err(bincode::error::DecodeError::EmptyEnum { type_name: core::any::type_name::<Self>() })")?;
285+
} else {
286+
fn_builder
287+
.push_parsed("let variant_index = <u32 as bincode::Decode>::decode(&mut decoder)?;")?;
288+
fn_builder.push_parsed("match variant_index")?;
289+
fn_builder.group(Delimiter::Brace, |variant_case| {
290+
for (mut variant_index, variant) in self.iter_fields() {
291+
// idx => Ok(..)
292+
if variant_index.len() > 1 {
293+
variant_case.push_parsed("x if x == ")?;
294+
variant_case.extend(variant_index);
295+
} else {
296+
variant_case.push(variant_index.remove(0));
297+
}
298+
variant_case.puncts("=>");
299+
variant_case.ident_str("Ok");
300+
variant_case.group(Delimiter::Parenthesis, |variant_case_body| {
301+
// Self::Variant { }
302+
// Self::Variant { 0: ..., 1: ... 2: ... },
303+
// Self::Variant { a: ..., b: ... c: ... },
304+
variant_case_body.ident_str("Self");
305+
variant_case_body.puncts("::");
306+
variant_case_body.ident(variant.name.clone());
291307

292-
variant_case_body.group(Delimiter::Brace, |variant_body| {
293-
let is_tuple = matches!(variant.fields, Fields::Tuple(_));
294-
for (idx, field) in variant.fields.names().into_iter().enumerate() {
295-
if is_tuple {
296-
variant_body.lit_usize(idx);
297-
} else {
298-
variant_body.ident(field.unwrap_ident().clone());
308+
variant_case_body.group(Delimiter::Brace, |variant_body| {
309+
let is_tuple = matches!(variant.fields, Fields::Tuple(_));
310+
for (idx, field) in variant.fields.names().into_iter().enumerate() {
311+
if is_tuple {
312+
variant_body.lit_usize(idx);
313+
} else {
314+
variant_body.ident(field.unwrap_ident().clone());
315+
}
316+
variant_body.punct(':');
317+
if field.attributes().has_attribute(FieldAttribute::WithSerde)? {
318+
variant_body
319+
.push_parsed("<bincode::serde::BorrowCompat<_> as bincode::BorrowDecode>::borrow_decode(&mut decoder)?.0,")?;
320+
} else {
321+
variant_body.push_parsed("bincode::de::BorrowDecode::borrow_decode(&mut decoder)?,")?;
322+
}
299323
}
300-
variant_body.punct(':');
301-
if field.attributes().has_attribute(FieldAttribute::WithSerde)? {
302-
variant_body
303-
.push_parsed("<bincode::serde::BorrowCompat<_> as bincode::BorrowDecode>::borrow_decode(&mut decoder)?.0,")?;
304-
} else {
305-
variant_body.push_parsed("bincode::de::BorrowDecode::borrow_decode(&mut decoder)?,")?;
306-
}
307-
}
324+
Ok(())
325+
})?;
308326
Ok(())
309327
})?;
310-
Ok(())
311-
})?;
312-
variant_case.punct(',');
313-
}
328+
variant_case.punct(',');
329+
}
314330

315-
// invalid idx
316-
self.invalid_variant_case(&enum_name, variant_case)
317-
})?;
331+
// invalid idx
332+
self.invalid_variant_case(&enum_name, variant_case)
333+
})?;
334+
}
318335
Ok(())
319336
})?;
320337
Ok(())

src/error.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,12 @@ pub enum DecodeError {
116116
found: usize,
117117
},
118118

119+
/// Tried to decode an enum with no variants
120+
EmptyEnum {
121+
/// The type that was being decoded
122+
type_name: &'static str,
123+
},
124+
119125
/// The decoder tried to decode a `CStr` or `CString`, but the incoming data contained a 0 byte
120126
#[cfg(feature = "std")]
121127
CStrNulError {

tests/derive.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,3 +251,21 @@ fn test_macro_newtype() {
251251
assert_eq!(len, newtype_len);
252252
}
253253
}
254+
255+
#[derive(bincode::Encode, bincode::Decode, Debug)]
256+
pub enum EmptyEnum {}
257+
258+
#[derive(bincode::Encode, bincode::BorrowDecode, Debug)]
259+
pub enum BorrowedEmptyEnum {}
260+
261+
#[test]
262+
fn test_empty_enum_decode() {
263+
let err =
264+
bincode::decode_from_slice::<EmptyEnum, _>(&[], Configuration::standard()).unwrap_err();
265+
assert_eq!(
266+
err,
267+
bincode::error::DecodeError::EmptyEnum {
268+
type_name: "derive::EmptyEnum"
269+
}
270+
);
271+
}

0 commit comments

Comments
 (0)