#![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>,
    /// Visbility
    vis: Visibility,

    /// 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(),
            vis: input.vis.clone(),
            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 }
                    }
                }

                #[automatically_derived]
                #[doc(hidden)]
                impl __EnumSetConstHelper {
                    pub const fn const_union(
                        &self,
                        chain_a: #enumset::EnumSet<#name>,
                        chain_b: #enumset::EnumSet<#name>,
                    ) -> #enumset::EnumSet<#name> {
                        #enumset::EnumSet {
                            __priv_repr: chain_a.__priv_repr | chain_b.__priv_repr,
                        }
                    }

                    pub const fn const_intersection(
                        &self,
                        chain_a: #enumset::EnumSet<#name>,
                        chain_b: #enumset::EnumSet<#name>,
                    ) -> #enumset::EnumSet<#name> {
                        #enumset::EnumSet {
                            __priv_repr: chain_a.__priv_repr & chain_b.__priv_repr,
                        }
                    }

                    pub const fn const_symmetric_difference(
                        &self,
                        chain_a: #enumset::EnumSet<#name>,
                        chain_b: #enumset::EnumSet<#name>,
                    ) -> #enumset::EnumSet<#name> {
                        #enumset::EnumSet {
                            __priv_repr: chain_a.__priv_repr ^ chain_b.__priv_repr,
                        }
                    }

                    pub const fn const_complement(
                        &self,
                        chain: #enumset::EnumSet<#name>,
                    ) -> #enumset::EnumSet<#name> {
                        let mut all = #enumset::EnumSet::<#name>::all();
                        #enumset::EnumSet {
                            __priv_repr: !chain.__priv_repr & all.__priv_repr,
                        }
                    }
                }
            }
        }
        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
                    }
                }

                #[automatically_derived]
                #[doc(hidden)]
                impl __EnumSetConstHelper {
                    pub const fn const_union(
                        &self,
                        mut chain_a: #enumset::EnumSet<#name>,
                        chain_b: #enumset::EnumSet<#name>,
                    ) -> #enumset::EnumSet<#name> {
                        let mut i = 0;
                        while i < #size {
                            chain_a.__priv_repr.0[i] |= chain_b.__priv_repr.0[i];
                            i += 1;
                        }
                        chain_a
                    }

                    pub const fn const_intersection(
                        &self,
                        mut chain_a: #enumset::EnumSet<#name>,
                        chain_b: #enumset::EnumSet<#name>,
                    ) -> #enumset::EnumSet<#name> {
                        let mut i = 0;
                        while i < #size {
                            chain_a.__priv_repr.0[i] &= chain_b.__priv_repr.0[i];
                            i += 1;
                        }
                        chain_a
                    }

                    pub const fn const_symmetric_difference(
                        &self,
                        mut chain_a: #enumset::EnumSet<#name>,
                        chain_b: #enumset::EnumSet<#name>,
                    ) -> #enumset::EnumSet<#name> {
                        let mut i = 0;
                        while i < #size {
                            chain_a.__priv_repr.0[i] ^= chain_b.__priv_repr.0[i];
                            i += 1;
                        }
                        chain_a
                    }

                    pub const fn const_complement(
                        &self,
                        mut chain: #enumset::EnumSet<#name>,
                    ) -> #enumset::EnumSet<#name> {
                        let mut all = #enumset::EnumSet::<#name>::all();
                        let mut i = 0;
                        while i < #size {
                            let new = !chain.__priv_repr.0[i] & all.__priv_repr.0[i];
                            chain.__priv_repr.0[i] = new;
                            i += 1;
                        }
                        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;
    let vis = &info.vis;
    quote! {
        const _: () = {
            #[automatically_derived]
            #[doc(hidden)]
            #vis struct __EnumSetConstHelper;

            #[automatically_derived]
            #[doc(hidden)]
            impl #name {
                /// Creates a new enumset helper.
                #[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_helper(
                    self,
                ) -> __EnumSetConstHelper {
                    __EnumSetConstHelper
                }
            }

            #[automatically_derived]
            unsafe impl #internal::EnumSetTypePrivate for #name {
                type ConstHelper = __EnumSetConstHelper;
                const CONST_HELPER_INSTANCE: __EnumSetConstHelper = __EnumSetConstHelper;

                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
            #ops
            #inherent_impl_blocks

            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 input_span = input.span();
    let attrs: EnumsetAttrs = match EnumsetAttrs::from_derive_input(&input) {
        Ok(attrs) => attrs,
        Err(e) => return e.write_errors().into(),
    };
    derive_enum_set_type_0(input, attrs, input_span).unwrap_or_else(|e| e.to_compile_error().into())
}
fn derive_enum_set_type_0(
    input: DeriveInput,
    attrs: EnumsetAttrs,
    _input_span: Span,
) -> 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.",
            ));
        }
        #[cfg(feature = "std_deprecation_warning")]
        {
            warnings.push((
                _input_span,
                "feature = \"std\" is depercated. If you rename `enumset`, use \
                 feature = \"proc-macro-crate\" instead. If you don't, remove the feature.",
            ));
        }

        // 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")
    }
}
