#![recursion_limit = "256"] extern crate proc_macro; use darling::util::SpannedValue; use darling::*; use proc_macro::TokenStream; use proc_macro2::{Literal, Span, TokenStream as SynTokenStream}; use quote::*; use std::{collections::HashSet, fmt::Display}; use syn::spanned::Spanned; use syn::{Error, Result, *}; /// Helper function for emitting compile errors. fn error(span: Span, message: impl Display) -> Result { Err(Error::new(span, message)) } /// Decodes the custom attributes for our custom derive. #[derive(FromDeriveInput, Default)] #[darling(attributes(enumset), default)] struct EnumsetAttrs { no_ops: bool, no_super_impls: bool, #[darling(default)] repr: SpannedValue>, #[darling(default)] serialize_repr: SpannedValue>, serialize_deny_unknown: bool, #[darling(default)] crate_name: Option, // legacy options serialize_as_list: SpannedValue, // replaced with serialize_repr serialize_as_map: SpannedValue, // replaced with serialize_repr } /// The internal representation of an enumset. #[derive(Copy, Clone)] enum InternalRepr { /// internal repr: `u8` U8, /// internal repr: `u16` U16, /// internal repr: `u32` U32, /// internal repr: `u64` U64, /// internal repr: `u128` U128, /// internal repr: `[u64; size]` Array(usize), } impl InternalRepr { /// Determines the number of variants supported by this repr. fn supported_variants(&self) -> usize { match self { InternalRepr::U8 => 8, InternalRepr::U16 => 16, InternalRepr::U32 => 32, InternalRepr::U64 => 64, InternalRepr::U128 => 128, InternalRepr::Array(size) => size * 64, } } } /// The serde representation of the enumset. #[derive(Copy, Clone)] enum SerdeRepr { /// serde type: `u8` U8, /// serde type: `u16` U16, /// serde type: `u32` U32, /// serde type: `u64` U64, /// serde type: `u128` U128, /// serde type: list of `T` List, /// serde type: map of `T` to `bool` Map, /// serde type: list of `u64` Array, } impl SerdeRepr { /// Determines the number of variants supported by this repr. fn supported_variants(&self) -> Option { match self { SerdeRepr::U8 => Some(8), SerdeRepr::U16 => Some(16), SerdeRepr::U32 => Some(32), SerdeRepr::U64 => Some(64), SerdeRepr::U128 => Some(128), SerdeRepr::List => None, SerdeRepr::Map => None, SerdeRepr::Array => None, } } } /// An variant in the enum set type. struct EnumSetValue { /// The name of the variant. name: Ident, /// The discriminant of the variant. variant_repr: u32, } /// Stores information about the enum set type. #[allow(dead_code)] struct EnumSetInfo { /// The name of the enum. name: Ident, /// The crate name to use. crate_name: Option, /// The numeric type to represent the `EnumSet` as in memory. explicit_internal_repr: Option, /// Forces the internal numeric type of the `EnumSet` to be an array. internal_repr_force_array: bool, /// The numeric type to serialize the enum as. explicit_serde_repr: Option, /// A list of variants in the enum. variants: Vec, /// The highest encountered variant discriminant. max_discrim: u32, /// The span of the highest encountered variant. max_discrim_span: Option, /// The current variant discriminant. Used to track, e.g. `A=10,B,C`. cur_discrim: u32, /// A list of variant names that are already in use. used_variant_names: HashSet, /// A list of variant discriminants that are already in use. used_discriminants: HashSet, /// Avoid generating operator overloads on the enum type. no_ops: bool, /// Avoid generating implementations for `Clone`, `Copy`, `Eq`, and `PartialEq`. no_super_impls: bool, /// Disallow unknown bits while deserializing the enum. serialize_deny_unknown: bool, } impl EnumSetInfo { fn new(input: &DeriveInput, attrs: &EnumsetAttrs) -> EnumSetInfo { EnumSetInfo { name: input.ident.clone(), crate_name: attrs .crate_name .as_ref() .map(|x| Ident::new(x, Span::call_site())), explicit_internal_repr: None, internal_repr_force_array: false, explicit_serde_repr: None, variants: Vec::new(), max_discrim: 0, max_discrim_span: None, cur_discrim: 0, used_variant_names: HashSet::new(), used_discriminants: HashSet::new(), no_ops: attrs.no_ops, no_super_impls: attrs.no_super_impls, serialize_deny_unknown: attrs.serialize_deny_unknown, } } /// Explicits sets the serde representation of the enumset from a string. fn push_serialize_repr(&mut self, span: Span, ty: &str) -> Result<()> { match ty { "u8" => self.explicit_serde_repr = Some(SerdeRepr::U8), "u16" => self.explicit_serde_repr = Some(SerdeRepr::U16), "u32" => self.explicit_serde_repr = Some(SerdeRepr::U32), "u64" => self.explicit_serde_repr = Some(SerdeRepr::U64), "u128" => self.explicit_serde_repr = Some(SerdeRepr::U128), "list" => self.explicit_serde_repr = Some(SerdeRepr::List), "map" => self.explicit_serde_repr = Some(SerdeRepr::Map), "array" => self.explicit_serde_repr = Some(SerdeRepr::Array), _ => error(span, format!("`{}` is not a valid serialized representation.", ty))?, } Ok(()) } /// Explicitly sets the representation of the enumset from a string. fn push_repr(&mut self, span: Span, ty: &str) -> Result<()> { match ty { "u8" => self.explicit_internal_repr = Some(InternalRepr::U8), "u16" => self.explicit_internal_repr = Some(InternalRepr::U16), "u32" => self.explicit_internal_repr = Some(InternalRepr::U32), "u64" => self.explicit_internal_repr = Some(InternalRepr::U64), "u128" => self.explicit_internal_repr = Some(InternalRepr::U128), "array" => self.internal_repr_force_array = true, _ => error(span, format!("`{}` is not a valid internal enumset representation.", ty))?, } Ok(()) } /// Adds a variant to the enumset. fn push_variant(&mut self, variant: &Variant) -> Result<()> { if self.used_variant_names.contains(&variant.ident.to_string()) { error(variant.span(), "Duplicated variant name.") } else if let Fields::Unit = variant.fields { // Parse the discriminant. if let Some((_, expr)) = &variant.discriminant { if let Expr::Lit(ExprLit { lit: Lit::Int(i), .. }) = expr { match i.base10_parse() { Ok(val) => self.cur_discrim = val, Err(_) => error(expr.span(), "Enum discriminants must fit into `u32`.")?, } } else if let Expr::Unary(ExprUnary { op: UnOp::Neg(_), .. }) = expr { error(expr.span(), "Enum discriminants must not be negative.")?; } else { error(variant.span(), "Enum discriminants must be literal expressions.")?; } } // Validate the discriminant. let discriminant = self.cur_discrim; if discriminant >= 0xFFFFFFC0 { error(variant.span(), "Maximum discriminant allowed is `0xFFFFFFBF`.")?; } if self.used_discriminants.contains(&discriminant) { error(variant.span(), "Duplicated enum discriminant.")?; } // Add the variant to the info. self.cur_discrim += 1; if discriminant > self.max_discrim { self.max_discrim = discriminant; self.max_discrim_span = Some(variant.span()); } self.variants .push(EnumSetValue { name: variant.ident.clone(), variant_repr: discriminant }); self.used_variant_names.insert(variant.ident.to_string()); self.used_discriminants.insert(discriminant); Ok(()) } else { error(variant.span(), "`#[derive(EnumSetType)]` can only be used on fieldless enums.") } } /// Returns the actual internal representation of the set. fn internal_repr(&self) -> InternalRepr { match self.explicit_internal_repr { Some(x) => x, None => match self.max_discrim { x if x < 8 && !self.internal_repr_force_array => InternalRepr::U8, x if x < 16 && !self.internal_repr_force_array => InternalRepr::U16, x if x < 32 && !self.internal_repr_force_array => InternalRepr::U32, x if x < 64 && !self.internal_repr_force_array => InternalRepr::U64, x => InternalRepr::Array((x as usize + 64) / 64), }, } } /// Returns the actual serde representation of the set. fn serde_repr(&self) -> SerdeRepr { match self.explicit_serde_repr { Some(x) => x, None => match self.max_discrim { x if x < 8 => SerdeRepr::U8, x if x < 16 => SerdeRepr::U16, x if x < 32 => SerdeRepr::U32, x if x < 64 => SerdeRepr::U64, x if x < 128 => SerdeRepr::U128, _ => SerdeRepr::Array, }, } } /// Validate the enumset type. fn validate(&self) -> Result<()> { // Gets the span of the maximum value. let largest_discriminant_span = match &self.max_discrim_span { Some(x) => *x, None => Span::call_site(), }; // Check if all bits of the bitset can fit in the memory representation, if one was given. if self.internal_repr().supported_variants() <= self.max_discrim as usize { error( largest_discriminant_span, "`repr` is too small to contain the largest discriminant.", )?; } // Check if all bits of the bitset can fit in the serialization representation. if let Some(supported_variants) = self.serde_repr().supported_variants() { if supported_variants <= self.max_discrim as usize { error( largest_discriminant_span, "`serialize_repr` is too small to contain the largest discriminant.", )?; } } Ok(()) } /// Returns a bitmask of all variants in the set. fn variant_map(&self) -> Vec { let mut vec = vec![0]; for variant in &self.variants { let (idx, bit) = (variant.variant_repr as usize / 64, variant.variant_repr % 64); while idx >= vec.len() { vec.push(0); } vec[idx] |= 1u64 << bit; } vec } } /// Generates the actual `EnumSetType` impl. fn enum_set_type_impl(info: EnumSetInfo, warnings: Vec<(Span, &'static str)>) -> SynTokenStream { let name = &info.name; let enumset = match &info.crate_name { Some(crate_name) => quote!(::#crate_name), None => { #[cfg(feature = "proc-macro-crate")] { use proc_macro_crate::FoundCrate; let crate_name = proc_macro_crate::crate_name("enumset"); match crate_name { Ok(FoundCrate::Name(name)) => { let ident = Ident::new(&name, Span::call_site()); quote!(::#ident) } _ => quote!(::enumset), } } #[cfg(not(feature = "proc-macro-crate"))] { quote!(::enumset) } } }; let typed_enumset = quote!(#enumset::EnumSet<#name>); let core = quote!(#enumset::__internal::core_export); let internal = quote!(#enumset::__internal); #[cfg(feature = "serde")] let serde = quote!(#enumset::__internal::serde); let repr = match info.internal_repr() { InternalRepr::U8 => quote! { u8 }, InternalRepr::U16 => quote! { u16 }, InternalRepr::U32 => quote! { u32 }, InternalRepr::U64 => quote! { u64 }, InternalRepr::U128 => quote! { u128 }, InternalRepr::Array(size) => quote! { #internal::ArrayRepr<{ #size }> }, }; let variant_map = info.variant_map(); let all_variants = match info.internal_repr() { InternalRepr::U8 | InternalRepr::U16 | InternalRepr::U32 | InternalRepr::U64 => { let lit = Literal::u64_unsuffixed(variant_map[0]); quote! { #lit } } InternalRepr::U128 => { let lit = Literal::u128_unsuffixed( variant_map[0] as u128 | variant_map.get(1).map_or(0, |x| (*x as u128) << 64), ); quote! { #lit } } InternalRepr::Array(size) => { let mut new = Vec::new(); for i in 0..size { new.push(Literal::u64_unsuffixed(*variant_map.get(i).unwrap_or(&0))); } quote! { #internal::ArrayRepr::<{ #size }>([#(#new,)*]) } } }; let ops = if info.no_ops { quote! {} } else { quote! { #[automatically_derived] impl> #core::ops::Sub for #name { type Output = #typed_enumset; fn sub(self, other: O) -> Self::Output { #enumset::EnumSet::only(self) - other.into() } } #[automatically_derived] impl> #core::ops::BitAnd for #name { type Output = #typed_enumset; fn bitand(self, other: O) -> Self::Output { #enumset::EnumSet::only(self) & other.into() } } #[automatically_derived] impl> #core::ops::BitOr for #name { type Output = #typed_enumset; fn bitor(self, other: O) -> Self::Output { #enumset::EnumSet::only(self) | other.into() } } #[automatically_derived] impl> #core::ops::BitXor for #name { type Output = #typed_enumset; fn bitxor(self, other: O) -> Self::Output { #enumset::EnumSet::only(self) ^ other.into() } } #[automatically_derived] impl #core::ops::Not for #name { type Output = #typed_enumset; fn not(self) -> Self::Output { !#enumset::EnumSet::only(self) } } #[automatically_derived] impl #core::cmp::PartialEq<#typed_enumset> for #name { fn eq(&self, other: &#typed_enumset) -> bool { #enumset::EnumSet::only(*self) == *other } } } }; #[cfg(feature = "serde")] let serde_repr = info.serde_repr(); #[cfg(feature = "serde")] let serde_ops = match serde_repr { SerdeRepr::U8 | SerdeRepr::U16 | SerdeRepr::U32 | SerdeRepr::U64 | SerdeRepr::U128 => { let (serialize_repr, from_fn, to_fn) = match serde_repr { SerdeRepr::U8 => (quote! { u8 }, quote! { from_u8 }, quote! { to_u8 }), SerdeRepr::U16 => (quote! { u16 }, quote! { from_u16 }, quote! { to_u16 }), SerdeRepr::U32 => (quote! { u32 }, quote! { from_u32 }, quote! { to_u32 }), SerdeRepr::U64 => (quote! { u64 }, quote! { from_u64 }, quote! { to_u64 }), SerdeRepr::U128 => (quote! { u128 }, quote! { from_u128 }, quote! { to_u128 }), _ => unreachable!(), }; let check_unknown = if info.serialize_deny_unknown { quote! { if value & !#all_variants != 0 { use #serde::de::Error; return #core::prelude::v1::Err( D::Error::custom("enumset contains unknown bits") ) } } } else { quote! {} }; quote! { fn serialize( set: #enumset::EnumSet<#name>, ser: S, ) -> #core::result::Result { let value = <#repr as #enumset::__internal::EnumSetTypeRepr>::#to_fn(&set.__priv_repr); #serde::Serialize::serialize(&value, ser) } fn deserialize<'de, D: #serde::Deserializer<'de>>( de: D, ) -> #core::result::Result<#enumset::EnumSet<#name>, D::Error> { let value = <#serialize_repr as #serde::Deserialize>::deserialize(de)?; #check_unknown let value = <#repr as #enumset::__internal::EnumSetTypeRepr>::#from_fn(value); #core::prelude::v1::Ok(#enumset::EnumSet { __priv_repr: value & #all_variants, }) } } } SerdeRepr::List => { let expecting_str = format!("a list of {}", name); quote! { fn serialize( set: #enumset::EnumSet<#name>, ser: S, ) -> #core::result::Result { use #serde::ser::SerializeSeq; let mut seq = ser.serialize_seq(#core::prelude::v1::Some(set.len()))?; for bit in set { seq.serialize_element(&bit)?; } seq.end() } fn deserialize<'de, D: #serde::Deserializer<'de>>( de: D, ) -> #core::result::Result<#enumset::EnumSet<#name>, D::Error> { struct Visitor; impl <'de> #serde::de::Visitor<'de> for Visitor { type Value = #enumset::EnumSet<#name>; fn expecting( &self, formatter: &mut #core::fmt::Formatter, ) -> #core::fmt::Result { write!(formatter, #expecting_str) } fn visit_seq( mut self, mut seq: A, ) -> #core::result::Result where A: #serde::de::SeqAccess<'de> { let mut accum = #enumset::EnumSet::<#name>::new(); while let #core::prelude::v1::Some(val) = seq.next_element::<#name>()? { accum |= val; } #core::prelude::v1::Ok(accum) } } de.deserialize_seq(Visitor) } } } SerdeRepr::Map => { let expecting_str = format!("a map from {} to bool", name); quote! { fn serialize( set: #enumset::EnumSet<#name>, ser: S, ) -> #core::result::Result { use #serde::ser::SerializeMap; let mut map = ser.serialize_map(#core::prelude::v1::Some(set.len()))?; for bit in set { map.serialize_entry(&bit, &true)?; } map.end() } fn deserialize<'de, D: #serde::Deserializer<'de>>( de: D, ) -> #core::result::Result<#enumset::EnumSet<#name>, D::Error> { struct Visitor; impl <'de> #serde::de::Visitor<'de> for Visitor { type Value = #enumset::EnumSet<#name>; fn expecting( &self, formatter: &mut #core::fmt::Formatter, ) -> #core::fmt::Result { write!(formatter, #expecting_str) } fn visit_map( mut self, mut map: A, ) -> #core::result::Result where A: #serde::de::MapAccess<'de> { let mut accum = #enumset::EnumSet::<#name>::new(); while let #core::prelude::v1::Some((val, is_present)) = map.next_entry::<#name, bool>()? { if is_present { accum |= val; } } #core::prelude::v1::Ok(accum) } } de.deserialize_map(Visitor) } } } SerdeRepr::Array => { let preferred_size = quote! { <<#name as #internal::EnumSetTypePrivate>::Repr as #internal::EnumSetTypeRepr> ::PREFERRED_ARRAY_LEN }; let (check_extra, convert_array) = if info.serialize_deny_unknown { ( quote! { if _val != 0 { return #core::prelude::v1::Err( D::Error::custom("enumset contains unknown bits") ) } }, quote! { match #enumset::EnumSet::<#name>::try_from_array(accum) { Some(x) => x, None => #core::prelude::v1::Err( D::Error::custom("enumset contains unknown bits") ), } }, ) } else { (quote! {}, quote! { #core::prelude::v1::Ok(#enumset::EnumSet::<#name>::from_array(accum)) }) }; quote! { fn serialize( set: #enumset::EnumSet<#name>, ser: S, ) -> #core::result::Result { // read the enum as an array let array = set.as_array::<{ #preferred_size }>(); // find the last non-zero value in the array let mut end = array.len(); for i in (0..array.len()).rev() { if array[i] != 0 { break; } end = i + 1; } // serialize the array #serde::Serialize::serialize(&array[..end], ser) } fn deserialize<'de, D: #serde::Deserializer<'de>>( de: D, ) -> #core::result::Result<#enumset::EnumSet<#name>, D::Error> { struct Visitor; impl <'de> #serde::de::Visitor<'de> for Visitor { type Value = #enumset::EnumSet<#name>; fn expecting( &self, formatter: &mut #core::fmt::Formatter, ) -> #core::fmt::Result { write!(formatter, "a list of u64") } fn visit_seq( mut self, mut seq: A, ) -> #core::result::Result where A: #serde::de::SeqAccess<'de> { let mut accum = [0; #preferred_size]; let mut i = 0; while let #core::prelude::v1::Some(val) = seq.next_element::()? { accum[i] = val; i += 1; if i == accum.len() { break; } } while let #core::prelude::v1::Some(_val) = seq.next_element::()? { #check_extra } #convert_array } } de.deserialize_seq(Visitor) } } } }; #[cfg(not(feature = "serde"))] let serde_ops = quote! {}; let is_uninhabited = info.variants.is_empty(); let is_zst = info.variants.len() == 1; let into_impl = if is_uninhabited { quote! { fn enum_into_u32(self) -> u32 { panic!(concat!(stringify!(#name), " is uninhabited.")) } unsafe fn enum_from_u32(val: u32) -> Self { panic!(concat!(stringify!(#name), " is uninhabited.")) } } } else if is_zst { let variant = &info.variants[0].name; quote! { fn enum_into_u32(self) -> u32 { self as u32 } unsafe fn enum_from_u32(val: u32) -> Self { #name::#variant } } } else { let variant_name: Vec<_> = info.variants.iter().map(|x| &x.name).collect(); let variant_value: Vec<_> = info.variants.iter().map(|x| x.variant_repr).collect(); let const_field: Vec<_> = ["IS_U8", "IS_U16", "IS_U32", "IS_U64", "IS_U128"] .iter() .map(|x| Ident::new(x, Span::call_site())) .collect(); let int_type: Vec<_> = ["u8", "u16", "u32", "u64", "u128"] .iter() .map(|x| Ident::new(x, Span::call_site())) .collect(); quote! { fn enum_into_u32(self) -> u32 { self as u32 } unsafe fn enum_from_u32(val: u32) -> Self { // We put these in const fields so the branches they guard aren't generated even // on -O0 #(const #const_field: bool = #core::mem::size_of::<#name>() == #core::mem::size_of::<#int_type>();)* match val { // Every valid variant value has an explicit branch. If they get optimized out, // great. If the representation has changed somehow, and they don't, oh well, // there's still no UB. #(#variant_value => #name::#variant_name,)* // Helps hint to the LLVM that this is a transmute. Note that this branch is // still unreachable. #(x if #const_field => { let x = x as #int_type; *(&x as *const _ as *const #name) })* // Default case. Sometimes causes LLVM to generate a table instead of a simple // transmute, but, oh well. _ => #core::hint::unreachable_unchecked(), } } } }; let eq_impl = if is_uninhabited { quote!(panic!(concat!(stringify!(#name), " is uninhabited."))) } else { quote!((*self as u32) == (*other as u32)) }; let super_impls = if info.no_super_impls { quote! {} } else { quote! { #[automatically_derived] impl #core::cmp::PartialEq for #name { fn eq(&self, other: &Self) -> bool { #eq_impl } } #[automatically_derived] impl #core::cmp::Eq for #name { } #[automatically_derived] #[allow(clippy::expl_impl_clone_on_copy)] impl #core::clone::Clone for #name { fn clone(&self) -> Self { *self } } #[automatically_derived] impl #core::marker::Copy for #name { } } }; let impl_with_repr = if info.explicit_internal_repr.is_some() { quote! { #[automatically_derived] unsafe impl #enumset::EnumSetTypeWithRepr for #name { type Repr = #repr; } } } else { quote! {} }; let inherent_impl_blocks = match info.internal_repr() { InternalRepr::U8 | InternalRepr::U16 | InternalRepr::U32 | InternalRepr::U64 | InternalRepr::U128 => { let self_as_repr_mask = if is_uninhabited { quote! { 0 } // impossible anyway } else { quote! { 1 << self as #repr } }; quote! { #[automatically_derived] #[doc(hidden)] impl #name { /// Creates a new enumset with only this variant. #[deprecated(note = "This method is an internal implementation detail \ generated by the `enumset` crate's procedural macro. It \ should not be used directly.")] #[doc(hidden)] pub const fn __impl_enumset_internal__const_only( self, ) -> #enumset::EnumSet<#name> { #enumset::EnumSet { __priv_repr: #self_as_repr_mask } } /// Creates a new enumset with this variant added. #[deprecated(note = "This method is an internal implementation detail \ generated by the `enumset` crate's procedural macro. It \ should not be used directly.")] #[doc(hidden)] pub const fn __impl_enumset_internal__const_merge( self, chain: #enumset::EnumSet<#name>, ) -> #enumset::EnumSet<#name> { #enumset::EnumSet { __priv_repr: chain.__priv_repr | #self_as_repr_mask } } } } } InternalRepr::Array(size) => { quote! { #[automatically_derived] #[doc(hidden)] impl #name { /// Creates a new enumset with only this variant. #[deprecated(note = "This method is an internal implementation detail \ generated by the `enumset` crate's procedural macro. It \ should not be used directly.")] #[doc(hidden)] pub const fn __impl_enumset_internal__const_only( self, ) -> #enumset::EnumSet<#name> { let mut set = #enumset::EnumSet::<#name> { __priv_repr: #internal::ArrayRepr::<{ #size }>([0; #size]), }; let bit = self as u32; let (idx, bit) = (bit as usize / 64, bit % 64); set.__priv_repr.0[idx] |= 1u64 << bit; set } /// Creates a new enumset with this variant added. #[deprecated(note = "This method is an internal implementation detail \ generated by the `enumset` crate's procedural macro. It \ should not be used directly.")] #[doc(hidden)] pub const fn __impl_enumset_internal__const_merge( self, mut chain: #enumset::EnumSet<#name>, ) -> #enumset::EnumSet<#name> { let bit = self as u32; let (idx, bit) = (bit as usize / 64, bit % 64); chain.__priv_repr.0[idx] |= 1u64 << bit; chain } } } } }; let mut generated_warnings = SynTokenStream::new(); for (span, warning) in warnings { generated_warnings.extend(quote_spanned! { span => { #[deprecated(note = #warning)] #[allow(non_upper_case_globals)] const _w: () = (); let _ = _w; } }); } let bit_width = info.max_discrim + 1; let variant_count = info.variants.len() as u32; quote! { #[automatically_derived] unsafe impl #internal::EnumSetTypePrivate for #name { type Repr = #repr; const ALL_BITS: Self::Repr = #all_variants; const BIT_WIDTH: u32 = #bit_width; const VARIANT_COUNT: u32 = #variant_count; #into_impl #serde_ops } #[automatically_derived] unsafe impl #enumset::EnumSetType for #name { } #impl_with_repr #super_impls #inherent_impl_blocks #ops const _: () = { fn __enumset_derive__generated_warnings() { #generated_warnings } }; } } #[proc_macro_derive(EnumSetType, attributes(enumset))] pub fn derive_enum_set_type(input: TokenStream) -> TokenStream { let input: DeriveInput = parse_macro_input!(input); let attrs: EnumsetAttrs = match EnumsetAttrs::from_derive_input(&input) { Ok(attrs) => attrs, Err(e) => return e.write_errors().into(), }; match derive_enum_set_type_0(input, attrs) { Ok(v) => v, Err(e) => e.to_compile_error().into(), } } fn derive_enum_set_type_0(input: DeriveInput, attrs: EnumsetAttrs) -> Result { if !input.generics.params.is_empty() { error( input.generics.span(), "`#[derive(EnumSetType)]` cannot be used on enums with type parameters.", ) } else if let Data::Enum(data) = &input.data { let mut info = EnumSetInfo::new(&input, &attrs); let mut warnings = Vec::new(); // Check enum repr for attr in &input.attrs { if attr.path().is_ident("repr") { let meta: Ident = attr.parse_args()?; match meta.to_string().as_str() { "C" | "Rust" => {} "u8" | "u16" | "u32" | "u64" | "u128" | "usize" => {} "i8" | "i16" | "i32" | "i64" | "i128" | "isize" => {} x => error( attr.span(), format!("`#[repr({})]` cannot be used on enumset variants.", x), )?, } } } // Parse internal representations if let Some(repr) = &*attrs.repr { info.push_repr(attrs.repr.span(), repr)?; } // Parse serialization representations if let Some(serialize_repr) = &*attrs.serialize_repr { info.push_serialize_repr(attrs.serialize_repr.span(), serialize_repr)?; } if *attrs.serialize_as_map { info.explicit_serde_repr = Some(SerdeRepr::Map); warnings.push(( attrs.serialize_as_map.span(), "#[enumset(serialize_as_map)] is deprecated. \ Use `#[enumset(serialize_repr = \"map\")]` instead.", )); } if *attrs.serialize_as_list { // in old versions, serialize_as_list will override serialize_as_map info.explicit_serde_repr = Some(SerdeRepr::List); warnings.push(( attrs.serialize_as_list.span(), "#[enumset(serialize_as_list)] is deprecated. \ Use `#[enumset(serialize_repr = \"list\")]` instead.", )); } // Parse enum variants for variant in &data.variants { info.push_variant(variant)?; } // Validate the enumset info.validate()?; // Generates the actual `EnumSetType` implementation Ok(enum_set_type_impl(info, warnings).into()) } else { error(input.span(), "`#[derive(EnumSetType)]` may only be used on enums") } }