diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /third_party/rust/enumset_derive/src/lib.rs | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/enumset_derive/src/lib.rs')
-rw-r--r-- | third_party/rust/enumset_derive/src/lib.rs | 949 |
1 files changed, 949 insertions, 0 deletions
diff --git a/third_party/rust/enumset_derive/src/lib.rs b/third_party/rust/enumset_derive/src/lib.rs new file mode 100644 index 0000000000..7508d7546d --- /dev/null +++ b/third_party/rust/enumset_derive/src/lib.rs @@ -0,0 +1,949 @@ +#![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<T>(span: Span, message: impl Display) -> Result<T> { + 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<Option<String>>, + #[darling(default)] + serialize_repr: SpannedValue<Option<String>>, + serialize_deny_unknown: bool, + #[darling(default)] + crate_name: Option<String>, + + // legacy options + serialize_as_list: SpannedValue<bool>, // replaced with serialize_repr + serialize_as_map: SpannedValue<bool>, // 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<usize> { + 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<Ident>, + /// The numeric type to represent the `EnumSet` as in memory. + explicit_internal_repr: Option<InternalRepr>, + /// 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<SerdeRepr>, + /// A list of variants in the enum. + variants: Vec<EnumSetValue>, + + /// The highest encountered variant discriminant. + max_discrim: u32, + /// The span of the highest encountered variant. + max_discrim_span: Option<Span>, + /// 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<String>, + /// A list of variant discriminants that are already in use. + used_discriminants: HashSet<u32>, + + /// 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<u64> { + 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<O: Into<#typed_enumset>> #core::ops::Sub<O> for #name { + type Output = #typed_enumset; + fn sub(self, other: O) -> Self::Output { + #enumset::EnumSet::only(self) - other.into() + } + } + #[automatically_derived] + impl<O: Into<#typed_enumset>> #core::ops::BitAnd<O> for #name { + type Output = #typed_enumset; + fn bitand(self, other: O) -> Self::Output { + #enumset::EnumSet::only(self) & other.into() + } + } + #[automatically_derived] + impl<O: Into<#typed_enumset>> #core::ops::BitOr<O> for #name { + type Output = #typed_enumset; + fn bitor(self, other: O) -> Self::Output { + #enumset::EnumSet::only(self) | other.into() + } + } + #[automatically_derived] + impl<O: Into<#typed_enumset>> #core::ops::BitXor<O> 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<S: #serde::Serializer>( + set: #enumset::EnumSet<#name>, ser: S, + ) -> #core::result::Result<S::Ok, S::Error> { + 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<S: #serde::Serializer>( + set: #enumset::EnumSet<#name>, ser: S, + ) -> #core::result::Result<S::Ok, S::Error> { + 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<A>( + mut self, mut seq: A, + ) -> #core::result::Result<Self::Value, A::Error> 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<S: #serde::Serializer>( + set: #enumset::EnumSet<#name>, ser: S, + ) -> #core::result::Result<S::Ok, S::Error> { + 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<A>( + mut self, mut map: A, + ) -> #core::result::Result<Self::Value, A::Error> 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<S: #serde::Serializer>( + set: #enumset::EnumSet<#name>, ser: S, + ) -> #core::result::Result<S::Ok, S::Error> { + // 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<A>( + mut self, mut seq: A, + ) -> #core::result::Result<Self::Value, A::Error> 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::<u64>()? { + accum[i] = val; + i += 1; + + if i == accum.len() { + break; + } + } + while let #core::prelude::v1::Some(_val) = seq.next_element::<u64>()? { + #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<TokenStream> { + 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") + } +} |