From 56324dfa5ea8dd8f1b54b0633dbdf35d3e2a5430 Mon Sep 17 00:00:00 2001 From: Daniel Wagner-Hall Date: Sat, 14 Aug 2021 20:11:05 +0100 Subject: [PATCH] Support standard #[default] attr for FromPrimitive https://github.com/rust-lang/rust/issues/87517 started standardising `#[default]` as an attribute on enum variants, support it compatibly identically to `#[num_enum(default)]`. --- num_enum/tests/from_primitive.rs | 20 ++++++ .../compile_fail/exhaustive_enum.stderr | 2 +- .../compile_fail/missing_default.stderr | 2 +- .../compile_fail/multiple_defaults.rs | 4 +- .../compile_fail/multiple_defaults.stderr | 8 +-- .../multiple_defaults_different_kinds.rs | 13 ++++ .../multiple_defaults_different_kinds.stderr | 5 ++ .../multiple_num_enum_defaults.rs | 13 ++++ .../multiple_num_enum_defaults.stderr | 5 ++ num_enum_derive/src/lib.rs | 61 +++++++++++-------- 10 files changed, 99 insertions(+), 34 deletions(-) create mode 100644 num_enum/tests/try_build/compile_fail/multiple_defaults_different_kinds.rs create mode 100644 num_enum/tests/try_build/compile_fail/multiple_defaults_different_kinds.stderr create mode 100644 num_enum/tests/try_build/compile_fail/multiple_num_enum_defaults.rs create mode 100644 num_enum/tests/try_build/compile_fail/multiple_num_enum_defaults.stderr diff --git a/num_enum/tests/from_primitive.rs b/num_enum/tests/from_primitive.rs index 3b486f3..2e09c84 100644 --- a/num_enum/tests/from_primitive.rs +++ b/num_enum/tests/from_primitive.rs @@ -28,6 +28,26 @@ fn has_from_primitive_number() { assert_eq!(two, Enum::NonZero); } +#[test] +fn has_from_primitive_number_standard_default_attribute() { + #[derive(Debug, Eq, PartialEq, FromPrimitive)] + #[repr(u8)] + enum Enum { + Zero = 0, + #[default] + NonZero = 1, + } + + let zero = Enum::from_primitive(0_u8); + assert_eq!(zero, Enum::Zero); + + let one = Enum::from_primitive(1_u8); + assert_eq!(one, Enum::NonZero); + + let two = Enum::from_primitive(2_u8); + assert_eq!(two, Enum::NonZero); +} + #[test] fn from_primitive_number() { #[derive(Debug, Eq, PartialEq, FromPrimitive)] diff --git a/num_enum/tests/try_build/compile_fail/exhaustive_enum.stderr b/num_enum/tests/try_build/compile_fail/exhaustive_enum.stderr index 6ba5318..319ce79 100644 --- a/num_enum/tests/try_build/compile_fail/exhaustive_enum.stderr +++ b/num_enum/tests/try_build/compile_fail/exhaustive_enum.stderr @@ -1,4 +1,4 @@ -error: #[derive(FromPrimitive)] requires a variant marked with `#[num_enum(default)]` +error: #[derive(FromPrimitive)] requires a variant marked with `#[default]` or `#[num_enum(default)]` --> $DIR/exhaustive_enum.rs:1:10 | 1 | #[derive(num_enum::FromPrimitive)] diff --git a/num_enum/tests/try_build/compile_fail/missing_default.stderr b/num_enum/tests/try_build/compile_fail/missing_default.stderr index 2c0a3ad..81d546f 100644 --- a/num_enum/tests/try_build/compile_fail/missing_default.stderr +++ b/num_enum/tests/try_build/compile_fail/missing_default.stderr @@ -1,4 +1,4 @@ -error: #[derive(FromPrimitive)] requires a variant marked with `#[num_enum(default)]` +error: #[derive(FromPrimitive)] requires a variant marked with `#[default]` or `#[num_enum(default)]` --> $DIR/missing_default.rs:1:10 | 1 | #[derive(num_enum::FromPrimitive)] diff --git a/num_enum/tests/try_build/compile_fail/multiple_defaults.rs b/num_enum/tests/try_build/compile_fail/multiple_defaults.rs index f8a24e6..9f942d4 100644 --- a/num_enum/tests/try_build/compile_fail/multiple_defaults.rs +++ b/num_enum/tests/try_build/compile_fail/multiple_defaults.rs @@ -2,9 +2,9 @@ #[repr(u8)] enum Numbers { Zero, - #[num_enum(default)] + #[default] One, - #[num_enum(default)] + #[default] Two, } diff --git a/num_enum/tests/try_build/compile_fail/multiple_defaults.stderr b/num_enum/tests/try_build/compile_fail/multiple_defaults.stderr index 54bc02f..68e6add 100644 --- a/num_enum/tests/try_build/compile_fail/multiple_defaults.stderr +++ b/num_enum/tests/try_build/compile_fail/multiple_defaults.stderr @@ -1,5 +1,5 @@ -error: Multiple variants marked `#[num_enum(default)]` found - --> $DIR/multiple_defaults.rs:7:16 +error: Multiple variants marked `#[default]` or `#[num_enum(default)]` found + --> $DIR/multiple_defaults.rs:7:5 | -7 | #[num_enum(default)] - | ^^^^^^^ +7 | #[default] + | ^^^^^^^^^^ diff --git a/num_enum/tests/try_build/compile_fail/multiple_defaults_different_kinds.rs b/num_enum/tests/try_build/compile_fail/multiple_defaults_different_kinds.rs new file mode 100644 index 0000000..d1ecef2 --- /dev/null +++ b/num_enum/tests/try_build/compile_fail/multiple_defaults_different_kinds.rs @@ -0,0 +1,13 @@ +#[derive(num_enum::FromPrimitive, num_enum::TryFromPrimitive)] +#[repr(u8)] +enum Numbers { + Zero, + #[default] + One, + #[num_enum(default)] + Two, +} + +fn main() { + +} diff --git a/num_enum/tests/try_build/compile_fail/multiple_defaults_different_kinds.stderr b/num_enum/tests/try_build/compile_fail/multiple_defaults_different_kinds.stderr new file mode 100644 index 0000000..4e209ae --- /dev/null +++ b/num_enum/tests/try_build/compile_fail/multiple_defaults_different_kinds.stderr @@ -0,0 +1,5 @@ +error: Multiple variants marked `#[default]` or `#[num_enum(default)]` found + --> $DIR/multiple_defaults_different_kinds.rs:7:16 + | +7 | #[num_enum(default)] + | ^^^^^^^ diff --git a/num_enum/tests/try_build/compile_fail/multiple_num_enum_defaults.rs b/num_enum/tests/try_build/compile_fail/multiple_num_enum_defaults.rs new file mode 100644 index 0000000..f8a24e6 --- /dev/null +++ b/num_enum/tests/try_build/compile_fail/multiple_num_enum_defaults.rs @@ -0,0 +1,13 @@ +#[derive(num_enum::FromPrimitive, num_enum::TryFromPrimitive)] +#[repr(u8)] +enum Numbers { + Zero, + #[num_enum(default)] + One, + #[num_enum(default)] + Two, +} + +fn main() { + +} diff --git a/num_enum/tests/try_build/compile_fail/multiple_num_enum_defaults.stderr b/num_enum/tests/try_build/compile_fail/multiple_num_enum_defaults.stderr new file mode 100644 index 0000000..f481ee2 --- /dev/null +++ b/num_enum/tests/try_build/compile_fail/multiple_num_enum_defaults.stderr @@ -0,0 +1,5 @@ +error: Multiple variants marked `#[default]` or `#[num_enum(default)]` found + --> $DIR/multiple_num_enum_defaults.rs:7:16 + | +7 | #[num_enum(default)] + | ^^^^^^^ diff --git a/num_enum_derive/src/lib.rs b/num_enum_derive/src/lib.rs index cbe4e1e..6f17876 100644 --- a/num_enum_derive/src/lib.rs +++ b/num_enum_derive/src/lib.rs @@ -266,35 +266,44 @@ impl Parse for EnumInfo { let mut is_default: bool = false; for attribute in variant.attrs { - if !attribute.path.is_ident("num_enum") { - continue; - } - match attribute.parse_args_with(NumEnumVariantAttributes::parse) { - Ok(variant_attributes) => { - for variant_attribute in variant_attributes.items.iter() { - match variant_attribute { - NumEnumVariantAttributeItem::Default(default) => { - if has_default_variant { - die!(default.keyword => - "Multiple variants marked `#[num_enum(default)]` found" - ); + if attribute.path.is_ident("default") { + if has_default_variant { + die!(attribute => + "Multiple variants marked `#[default]` or `#[num_enum(default)]` found" + ); + } + attr_spans.default.push(attribute.span()); + is_default = true; + } else if attribute.path.is_ident("num_enum") { + match attribute.parse_args_with(NumEnumVariantAttributes::parse) { + Ok(variant_attributes) => { + for variant_attribute in variant_attributes.items.iter() { + match variant_attribute { + NumEnumVariantAttributeItem::Default(default) => { + if has_default_variant { + die!(default.keyword => + "Multiple variants marked `#[default]` or `#[num_enum(default)]` found" + ); + } + attr_spans.default.push(default.span()); + is_default = true; + } + NumEnumVariantAttributeItem::Alternatives(alternatives) => { + attr_spans.alternatives.push(alternatives.span()); + alternative_values + .extend(alternatives.expressions.iter().cloned()); } - attr_spans.default.push(default.span()); - is_default = true; - } - NumEnumVariantAttributeItem::Alternatives(alternatives) => { - attr_spans.alternatives.push(alternatives.span()); - alternative_values - .extend(alternatives.expressions.iter().cloned()); } } } + Err(err) => { + die!(attribute => + format!("Invalid attribute: {}", err) + ); + } } - Err(err) => { - die!(attribute => - format!("Invalid attribute: {}", err) - ); - } + } else { + continue; } has_default_variant |= is_default; @@ -387,7 +396,7 @@ pub fn derive_into_primitive(input: TokenStream) -> TokenStream { /// assert_eq!(two, Number::NonZero); /// } /// ``` -#[proc_macro_derive(FromPrimitive, attributes(num_enum))] +#[proc_macro_derive(FromPrimitive, attributes(num_enum, default))] pub fn derive_from_primitive(input: TokenStream) -> TokenStream { let enum_info: EnumInfo = parse_macro_input!(input); let krate = Ident::new(&get_crate_name(), Span::call_site()); @@ -397,7 +406,7 @@ pub fn derive_from_primitive(input: TokenStream) -> TokenStream { None => { let span = Span::call_site(); let message = - "#[derive(FromPrimitive)] requires a variant marked with `#[num_enum(default)]`"; + "#[derive(FromPrimitive)] requires a variant marked with `#[default]` or `#[num_enum(default)]`"; return syn::Error::new(span, message).to_compile_error().into(); } };