From 8aff7905ead7814d7cd0a40e94d784c1198e6171 Mon Sep 17 00:00:00 2001 From: Jason Scatena Date: Tue, 1 Aug 2023 17:29:40 -0400 Subject: [PATCH 1/4] moved repr extraction to helpers --- strum_macros/src/helpers/type_props.rs | 12 +++++ strum_macros/src/macros/from_repr.rs | 66 +++++++------------------- 2 files changed, 30 insertions(+), 48 deletions(-) diff --git a/strum_macros/src/helpers/type_props.rs b/strum_macros/src/helpers/type_props.rs index 0d49e04e..17d84c98 100644 --- a/strum_macros/src/helpers/type_props.rs +++ b/strum_macros/src/helpers/type_props.rs @@ -21,6 +21,7 @@ pub struct StrumTypeProperties { pub discriminant_others: Vec, pub discriminant_vis: Option, pub use_phf: bool, + pub enum_repr: Option, } impl HasTypeProperties for DeriveInput { @@ -103,6 +104,17 @@ impl HasTypeProperties for DeriveInput { } } + let attrs = &self.attrs; + for attr in attrs { + if let Ok(list) = attr.meta.require_list() { + if let Some(ident) = list.path.get_ident() { + if ident == "repr" { + output.enum_repr = Some(list.tokens.clone()) + } + } + } + } + Ok(output) } } diff --git a/strum_macros/src/macros/from_repr.rs b/strum_macros/src/macros/from_repr.rs index 92fd7ad9..a70c6f86 100644 --- a/strum_macros/src/macros/from_repr.rs +++ b/strum_macros/src/macros/from_repr.rs @@ -1,62 +1,32 @@ use heck::ToShoutySnakeCase; use proc_macro2::{Span, TokenStream}; -use quote::{format_ident, quote, ToTokens}; -use syn::{Data, DeriveInput, Fields, PathArguments, Type, TypeParen}; +use quote::{format_ident, quote}; +use syn::{Data, DeriveInput, Fields, Type}; -use crate::helpers::{non_enum_error, HasStrumVariantProperties}; +use crate::helpers::{non_enum_error, HasStrumVariantProperties, HasTypeProperties}; pub fn from_repr_inner(ast: &DeriveInput) -> syn::Result { let name = &ast.ident; let gen = &ast.generics; let (impl_generics, ty_generics, where_clause) = gen.split_for_impl(); let vis = &ast.vis; - let attrs = &ast.attrs; let mut discriminant_type: Type = syn::parse("usize".parse().unwrap()).unwrap(); - for attr in attrs { - let path = attr.path(); - - let mut ts = if let Ok(ts) = attr - .meta - .require_list() - .map(|metas| metas.to_token_stream().into_iter()) - { - ts - } else { - continue; - }; - // Discard the path - let _ = ts.next(); - let tokens: TokenStream = ts.collect(); - - if path.leading_colon.is_some() { - continue; - } - if path.segments.len() != 1 { - continue; - } - let segment = path.segments.first().unwrap(); - if segment.ident != "repr" { - continue; - } - if segment.arguments != PathArguments::None { - continue; - } - let typ_paren = match syn::parse2::(tokens.clone()) { - Ok(Type::Paren(TypeParen { elem, .. })) => *elem, - _ => continue, - }; - let inner_path = match &typ_paren { - Type::Path(t) => t, - _ => continue, - }; - if let Some(seg) = inner_path.path.segments.last() { - for t in &[ - "u8", "u16", "u32", "u64", "usize", "i8", "i16", "i32", "i64", "isize", - ] { - if seg.ident == t { - discriminant_type = typ_paren; - break; + if let Some(type_path) = ast + .get_type_properties() + .ok() + .and_then(|tp| tp.enum_repr) + .and_then(|repr_ts| syn::parse2::(repr_ts).ok()) + { + if let Type::Path(path) = type_path.clone() { + if let Some(seg) = path.path.segments.last() { + for t in &[ + "u8", "u16", "u32", "u64", "usize", "i8", "i16", "i32", "i64", "isize", + ] { + if seg.ident == t { + discriminant_type = type_path; + break; + } } } } From af98a6787420b3af06f5f4799b8639b04b840870 Mon Sep 17 00:00:00 2001 From: Jason Scatena Date: Tue, 1 Aug 2023 18:13:29 -0400 Subject: [PATCH 2/4] repr pass-through added --- strum_macros/src/macros/enum_discriminants.rs | 3 ++ strum_tests/src/lib.rs | 1 + strum_tests/tests/enum_discriminants.rs | 38 ++++++++++++++++++- 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/strum_macros/src/macros/enum_discriminants.rs b/strum_macros/src/macros/enum_discriminants.rs index 4e54a30c..1e2da165 100644 --- a/strum_macros/src/macros/enum_discriminants.rs +++ b/strum_macros/src/macros/enum_discriminants.rs @@ -40,6 +40,8 @@ pub fn enum_discriminants_inner(ast: &DeriveInput) -> syn::Result { // Pass through all other attributes let pass_though_attributes = type_properties.discriminant_others; + let repr = type_properties.enum_repr.map(|repr| quote!(#[repr(#repr)])); + // Add the variants without fields, but exclude the `strum` meta item let mut discriminants = Vec::new(); for variant in variants { @@ -153,6 +155,7 @@ pub fn enum_discriminants_inner(ast: &DeriveInput) -> syn::Result { Ok(quote! { /// Auto-generated discriminant enum variants #derives + #repr #(#[ #pass_though_attributes ])* #discriminants_vis enum #discriminants_name { #(#discriminants),* diff --git a/strum_tests/src/lib.rs b/strum_tests/src/lib.rs index 3addd0d6..98e7f476 100644 --- a/strum_tests/src/lib.rs +++ b/strum_tests/src/lib.rs @@ -1,6 +1,7 @@ use strum::{Display, EnumCount, EnumDiscriminants, EnumString}; #[derive(Debug, Eq, PartialEq, EnumString, Display, EnumCount, EnumDiscriminants)] +#[repr(u8)] pub enum Color { /// Docs on red #[strum(to_string = "RedRed")] diff --git a/strum_tests/tests/enum_discriminants.rs b/strum_tests/tests/enum_discriminants.rs index eb29e6d1..9bcfd377 100644 --- a/strum_tests/tests/enum_discriminants.rs +++ b/strum_tests/tests/enum_discriminants.rs @@ -1,7 +1,8 @@ +use std::mem::{align_of, size_of}; + use enum_variant_type::EnumVariantType; use strum::{Display, EnumDiscriminants, EnumIter, EnumMessage, EnumString, IntoEnumIterator}; - mod core {} // ensure macros call `::core` #[allow(dead_code)] @@ -305,3 +306,38 @@ fn crate_module_path_test() { assert_eq!(expected, discriminants); } + +#[allow(dead_code)] +#[derive(EnumDiscriminants)] +#[repr(u16)] +enum WithReprUInt { + Variant0, + Variant1, +} + +#[test] +fn with_repr_uint() { + // These tests would not be proof of proper functioning on a 16 bit system + assert_eq!(size_of::(), size_of::()); + assert_eq!( + size_of::(), + size_of::() + ) +} + +#[allow(dead_code)] +#[derive(EnumDiscriminants)] +#[repr(align(16), u8)] +enum WithReprAlign { + Variant0, + Variant1, +} + +#[test] +fn with_repr_align() { + assert_eq!( + align_of::(), + align_of::() + ); + assert_eq!(16, align_of::()); +} From 92c03a4def9302a349293e3f3cb001c0343de167 Mon Sep 17 00:00:00 2001 From: Jason Scatena Date: Tue, 1 Aug 2023 18:38:03 -0400 Subject: [PATCH 3/4] add discriminant pass through --- strum_macros/src/macros/enum_discriminants.rs | 6 ++++- strum_tests/tests/enum_discriminants.rs | 24 ++++++++++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/strum_macros/src/macros/enum_discriminants.rs b/strum_macros/src/macros/enum_discriminants.rs index 1e2da165..f7d63727 100644 --- a/strum_macros/src/macros/enum_discriminants.rs +++ b/strum_macros/src/macros/enum_discriminants.rs @@ -46,6 +46,10 @@ pub fn enum_discriminants_inner(ast: &DeriveInput) -> syn::Result { let mut discriminants = Vec::new(); for variant in variants { let ident = &variant.ident; + let discriminant = variant + .discriminant + .as_ref() + .map(|(_, expr)| quote!( = #expr)); // Don't copy across the "strum" meta attribute. Only passthrough the whitelisted // attributes and proxy `#[strum_discriminants(...)]` attributes @@ -83,7 +87,7 @@ pub fn enum_discriminants_inner(ast: &DeriveInput) -> syn::Result { }) .collect::, _>>()?; - discriminants.push(quote! { #(#attrs)* #ident }); + discriminants.push(quote! { #(#attrs)* #ident #discriminant}); } // Ideally: diff --git a/strum_tests/tests/enum_discriminants.rs b/strum_tests/tests/enum_discriminants.rs index 9bcfd377..d6e4668e 100644 --- a/strum_tests/tests/enum_discriminants.rs +++ b/strum_tests/tests/enum_discriminants.rs @@ -1,7 +1,9 @@ use std::mem::{align_of, size_of}; use enum_variant_type::EnumVariantType; -use strum::{Display, EnumDiscriminants, EnumIter, EnumMessage, EnumString, IntoEnumIterator}; +use strum::{ + Display, EnumDiscriminants, EnumIter, EnumMessage, EnumString, FromRepr, IntoEnumIterator, +}; mod core {} // ensure macros call `::core` @@ -341,3 +343,23 @@ fn with_repr_align() { ); assert_eq!(16, align_of::()); } + +#[allow(dead_code)] +#[derive(EnumDiscriminants)] +#[strum_discriminants(derive(FromRepr))] +enum WithExplicitDicriminantValue { + Variant0 = 42 + 100, + Variant1 = 11, +} + +#[test] +fn with_explicit_discriminant_value() { + assert_eq!( + WithExplicitDicriminantValueDiscriminants::from_repr(11), + Some(WithExplicitDicriminantValueDiscriminants::Variant1) + ); + assert_eq!( + 142, + WithExplicitDicriminantValueDiscriminants::Variant0 as u8 + ); +} From ebde3cb05aedfd975521176d3e2ef085790a7e19 Mon Sep 17 00:00:00 2001 From: Jason Scatena Date: Tue, 1 Aug 2023 18:44:44 -0400 Subject: [PATCH 4/4] remove dev artifact --- strum_tests/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/strum_tests/src/lib.rs b/strum_tests/src/lib.rs index 98e7f476..3addd0d6 100644 --- a/strum_tests/src/lib.rs +++ b/strum_tests/src/lib.rs @@ -1,7 +1,6 @@ use strum::{Display, EnumCount, EnumDiscriminants, EnumString}; #[derive(Debug, Eq, PartialEq, EnumString, Display, EnumCount, EnumDiscriminants)] -#[repr(u8)] pub enum Color { /// Docs on red #[strum(to_string = "RedRed")]