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))
}
/// 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> { matchself {
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>,
/// Explicits sets the serde representation of the enumset from a string. fn push_serialize_repr(&mutself, 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(&mutself, 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(&mutself, variant: &Variant) -> Result<()> { ifself.used_variant_names.contains(&variant.ident.to_string()) {
error(variant.span(), "Duplicated variant name.")
} elseiflet Fields::Unit = variant.fields { // Parse the discriminant. iflet Some((_, expr)) = &variant.discriminant { iflet 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`.")?,
}
} elseiflet 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`.")?;
} ifself.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 { matchself.explicit_internal_repr {
Some(x) => x,
None => matchself.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 { matchself.explicit_serde_repr {
Some(x) => x,
None => matchself.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. ifself.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. iflet 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> { letmut 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) => { letmut new = Vec::new(); for i in0..size {
new.push(Literal::u64_unsuffixed(*variant_map.get(i).unwrap_or(&0)));
}
quote! { #internal::ArrayRepr::<{ #size }>([#(#new,)*]) }
}
};
quote! { fn enum_into_u32(self) -> u32 { selfas u32
} unsafefn 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!((*selfas u32) == (*other as u32))
};
let impl_with_repr = if info.explicit_internal_repr.is_some() {
quote! { #[automatically_derived] unsafeimpl#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 << selfas#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)] pubconstfn __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)] pubconstfn __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)] pubconstfn __impl_enumset_internal__const_only( self,
) -> #enumset::EnumSet<#name> { letmut set = #enumset::EnumSet::<#name> {
__priv_repr: #internal::ArrayRepr::<{ #size }>([0; #size]),
}; let bit = selfas 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)] pubconstfn __impl_enumset_internal__const_merge( self, mut chain: #enumset::EnumSet<#name>,
) -> #enumset::EnumSet<#name> { let bit = selfas u32; let (idx, bit) = (bit as usize / 64, bit % 64);
chain.__priv_repr.0[idx] |= 1u64 << bit;
chain
}
}
}
}
};
// Parse serialization representations iflet 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")
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.17 Sekunden
(vorverarbeitet am 2026-06-19)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.