Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

View File

@@ -0,0 +1,274 @@
use crate::utils::die;
use proc_macro2::Span;
use syn::{
parse::{Parse, ParseStream},
Error, Result,
};
mod kw {
syn::custom_keyword!(constructor);
syn::custom_keyword!(error_type);
syn::custom_keyword!(name);
}
// Example: error_type(name = Foo, constructor = Foo::new)
#[cfg_attr(test, derive(Debug))]
pub(crate) struct Attributes {
pub(crate) error_type: Option<ErrorTypeAttribute>,
}
// Example: error_type(name = Foo, constructor = Foo::new)
#[cfg_attr(test, derive(Debug))]
pub(crate) enum AttributeItem {
ErrorType(ErrorTypeAttribute),
}
impl Parse for Attributes {
fn parse(input: ParseStream<'_>) -> Result<Self> {
let attribute_items = input.parse_terminated(AttributeItem::parse, syn::Token![,])?;
let mut maybe_error_type = None;
for attribute_item in &attribute_items {
match attribute_item {
AttributeItem::ErrorType(error_type) => {
if maybe_error_type.is_some() {
return Err(Error::new(
error_type.span,
"num_enum attribute must have at most one error_type",
));
}
maybe_error_type = Some(error_type.clone());
}
}
}
Ok(Self {
error_type: maybe_error_type,
})
}
}
impl Parse for AttributeItem {
fn parse(input: ParseStream<'_>) -> Result<Self> {
let lookahead = input.lookahead1();
if lookahead.peek(kw::error_type) {
input.parse().map(Self::ErrorType)
} else {
Err(lookahead.error())
}
}
}
// Example: error_type(name = Foo, constructor = Foo::new)
#[derive(Clone)]
#[cfg_attr(test, derive(Debug))]
pub(crate) struct ErrorTypeAttribute {
pub(crate) name: ErrorTypeNameAttribute,
pub(crate) constructor: ErrorTypeConstructorAttribute,
span: Span,
}
impl Parse for ErrorTypeAttribute {
fn parse(input: ParseStream) -> Result<Self> {
let keyword: kw::error_type = input.parse()?;
let span = keyword.span;
let content;
syn::parenthesized!(content in input);
let attribute_values =
content.parse_terminated(ErrorTypeAttributeNamedArgument::parse, syn::Token![,])?;
let mut name = None;
let mut constructor = None;
for attribute_value in &attribute_values {
match attribute_value {
ErrorTypeAttributeNamedArgument::Name(name_attr) => {
if name.is_some() {
die!("num_enum error_type attribute must have exactly one `name` value");
}
name = Some(name_attr.clone());
}
ErrorTypeAttributeNamedArgument::Constructor(constructor_attr) => {
if constructor.is_some() {
die!("num_enum error_type attribute must have exactly one `constructor` value")
}
constructor = Some(constructor_attr.clone());
}
}
}
match (name, constructor) {
(None, None) => Err(Error::new(
span,
"num_enum error_type attribute requires `name` and `constructor` values",
)),
(Some(_), None) => Err(Error::new(
span,
"num_enum error_type attribute requires `constructor` value",
)),
(None, Some(_)) => Err(Error::new(
span,
"num_enum error_type attribute requires `name` value",
)),
(Some(name), Some(constructor)) => Ok(Self {
name,
constructor,
span,
}),
}
}
}
// Examples:
// * name = Foo
// * constructor = Foo::new
pub(crate) enum ErrorTypeAttributeNamedArgument {
Name(ErrorTypeNameAttribute),
Constructor(ErrorTypeConstructorAttribute),
}
impl Parse for ErrorTypeAttributeNamedArgument {
fn parse(input: ParseStream<'_>) -> Result<Self> {
let lookahead = input.lookahead1();
if lookahead.peek(kw::name) {
input.parse().map(Self::Name)
} else if lookahead.peek(kw::constructor) {
input.parse().map(Self::Constructor)
} else {
Err(lookahead.error())
}
}
}
// Example: name = Foo
#[derive(Clone)]
#[cfg_attr(test, derive(Debug))]
pub(crate) struct ErrorTypeNameAttribute {
pub(crate) path: syn::Path,
}
impl Parse for ErrorTypeNameAttribute {
fn parse(input: ParseStream) -> Result<Self> {
input.parse::<kw::name>()?;
input.parse::<syn::Token![=]>()?;
let path = input.parse()?;
Ok(Self { path })
}
}
// Example: constructor = Foo::new
#[derive(Clone)]
#[cfg_attr(test, derive(Debug))]
pub(crate) struct ErrorTypeConstructorAttribute {
pub(crate) path: syn::Path,
}
impl Parse for ErrorTypeConstructorAttribute {
fn parse(input: ParseStream) -> Result<Self> {
input.parse::<kw::constructor>()?;
input.parse::<syn::Token![=]>()?;
let path = input.parse()?;
Ok(Self { path })
}
}
#[cfg(test)]
mod test {
use crate::enum_attributes::Attributes;
use quote::ToTokens;
use syn::{parse_quote, Path};
#[test]
fn parse_num_enum_attr() {
let expected_name: Path = parse_quote! { Foo };
let expected_constructor: Path = parse_quote! { ::foo::Foo::<u8>::new };
let attributes: Attributes =
syn::parse_str("error_type(name = Foo, constructor = ::foo::Foo::<u8>::new)").unwrap();
assert!(attributes.error_type.is_some());
let error_type = attributes.error_type.unwrap();
assert_eq!(
error_type.name.path.to_token_stream().to_string(),
expected_name.to_token_stream().to_string()
);
assert_eq!(
error_type.constructor.path.to_token_stream().to_string(),
expected_constructor.to_token_stream().to_string()
);
}
#[test]
fn parse_num_enum_attr_swapped_order() {
let expected_name: Path = parse_quote! { Foo };
let expected_constructor: Path = parse_quote! { ::foo::Foo::<u8>::new };
let attributes: Attributes =
syn::parse_str("error_type(constructor = ::foo::Foo::<u8>::new, name = Foo)").unwrap();
assert!(attributes.error_type.is_some());
let error_type = attributes.error_type.unwrap();
assert_eq!(
error_type.name.path.to_token_stream().to_string(),
expected_name.to_token_stream().to_string()
);
assert_eq!(
error_type.constructor.path.to_token_stream().to_string(),
expected_constructor.to_token_stream().to_string()
);
}
#[test]
fn missing_constructor() {
let err = syn::parse_str::<Attributes>("error_type(name = Foo)").unwrap_err();
assert_eq!(
err.to_string(),
"num_enum error_type attribute requires `constructor` value"
);
}
#[test]
fn missing_name() {
let err = syn::parse_str::<Attributes>("error_type(constructor = Foo::new)").unwrap_err();
assert_eq!(
err.to_string(),
"num_enum error_type attribute requires `name` value"
);
}
#[test]
fn missing_both() {
let err = syn::parse_str::<Attributes>("error_type()").unwrap_err();
assert_eq!(
err.to_string(),
"num_enum error_type attribute requires `name` and `constructor` values"
);
}
#[test]
fn extra_attr() {
let err = syn::parse_str::<Attributes>(
"error_type(name = Foo, constructor = Foo::new, extra = unneeded)",
)
.unwrap_err();
assert_eq!(err.to_string(), "expected `name` or `constructor`");
}
#[test]
fn multiple_names() {
let err = syn::parse_str::<Attributes>(
"error_type(name = Foo, name = Foo, constructor = Foo::new)",
)
.unwrap_err();
assert_eq!(
err.to_string(),
"num_enum error_type attribute must have exactly one `name` value"
);
}
#[test]
fn multiple_constructors() {
let err = syn::parse_str::<Attributes>(
"error_type(name = Foo, constructor = Foo::new, constructor = Foo::new)",
)
.unwrap_err();
assert_eq!(
err.to_string(),
"num_enum error_type attribute must have exactly one `constructor` value"
);
}
}

363
vendor/num_enum_derive/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,363 @@
// Not supported by MSRV
#![allow(clippy::uninlined_format_args)]
extern crate proc_macro;
use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::quote;
use syn::{parse_macro_input, Expr, Ident};
mod enum_attributes;
mod parsing;
use parsing::{get_crate_name, EnumInfo};
mod utils;
mod variant_attributes;
/// Implements `Into<Primitive>` for a `#[repr(Primitive)] enum`.
///
/// (It actually implements `From<Enum> for Primitive`)
///
/// ## Allows turning an enum into a primitive.
///
/// ```rust
/// use num_enum::IntoPrimitive;
///
/// #[derive(IntoPrimitive)]
/// #[repr(u8)]
/// enum Number {
/// Zero,
/// One,
/// }
///
/// let zero: u8 = Number::Zero.into();
/// assert_eq!(zero, 0u8);
/// ```
#[proc_macro_derive(IntoPrimitive, attributes(num_enum, catch_all))]
pub fn derive_into_primitive(input: TokenStream) -> TokenStream {
let enum_info = parse_macro_input!(input as EnumInfo);
let catch_all = enum_info.catch_all();
let name = &enum_info.name;
let repr = &enum_info.repr;
let body = if let Some(catch_all_ident) = catch_all {
quote! {
match enum_value {
#name::#catch_all_ident(raw) => raw,
rest => unsafe { *(&rest as *const #name as *const Self) }
}
}
} else {
quote! { enum_value as Self }
};
TokenStream::from(quote! {
impl From<#name> for #repr {
#[inline]
fn from (enum_value: #name) -> Self
{
#body
}
}
})
}
/// Implements `From<Primitive>` for a `#[repr(Primitive)] enum`.
///
/// Turning a primitive into an enum with `from`.
/// ----------------------------------------------
///
/// ```rust
/// use num_enum::FromPrimitive;
///
/// #[derive(Debug, Eq, PartialEq, FromPrimitive)]
/// #[repr(u8)]
/// enum Number {
/// Zero,
/// #[num_enum(default)]
/// NonZero,
/// }
///
/// let zero = Number::from(0u8);
/// assert_eq!(zero, Number::Zero);
///
/// let one = Number::from(1u8);
/// assert_eq!(one, Number::NonZero);
///
/// let two = Number::from(2u8);
/// assert_eq!(two, Number::NonZero);
/// ```
#[proc_macro_derive(FromPrimitive, attributes(num_enum, default, catch_all))]
pub fn derive_from_primitive(input: TokenStream) -> TokenStream {
let enum_info: EnumInfo = parse_macro_input!(input);
let krate = Ident::new(&get_crate_name(), Span::call_site());
let is_naturally_exhaustive = enum_info.is_naturally_exhaustive();
let catch_all_body = match is_naturally_exhaustive {
Ok(is_naturally_exhaustive) => {
if is_naturally_exhaustive {
quote! { unreachable!("exhaustive enum") }
} else if let Some(default_ident) = enum_info.default() {
quote! { Self::#default_ident }
} else if let Some(catch_all_ident) = enum_info.catch_all() {
quote! { Self::#catch_all_ident(number) }
} else {
let span = Span::call_site();
let message =
"#[derive(num_enum::FromPrimitive)] requires enum to be exhaustive, or a variant marked with `#[default]`, `#[num_enum(default)]`, or `#[num_enum(catch_all)`";
return syn::Error::new(span, message).to_compile_error().into();
}
}
Err(err) => {
return err.to_compile_error().into();
}
};
let EnumInfo {
ref name, ref repr, ..
} = enum_info;
let variant_idents: Vec<Ident> = enum_info.variant_idents();
let expression_idents: Vec<Vec<Ident>> = enum_info.expression_idents();
let variant_expressions: Vec<Vec<Expr>> = enum_info.variant_expressions();
debug_assert_eq!(variant_idents.len(), variant_expressions.len());
TokenStream::from(quote! {
impl ::#krate::FromPrimitive for #name {
type Primitive = #repr;
fn from_primitive(number: Self::Primitive) -> Self {
// Use intermediate const(s) so that enums defined like
// `Two = ONE + 1u8` work properly.
#![allow(non_upper_case_globals)]
#(
#(
const #expression_idents: #repr = #variant_expressions;
)*
)*
#[deny(unreachable_patterns)]
match number {
#(
#( #expression_idents )|*
=> Self::#variant_idents,
)*
#[allow(unreachable_patterns)]
_ => #catch_all_body,
}
}
}
impl ::core::convert::From<#repr> for #name {
#[inline]
fn from (
number: #repr,
) -> Self {
::#krate::FromPrimitive::from_primitive(number)
}
}
#[doc(hidden)]
impl ::#krate::CannotDeriveBothFromPrimitiveAndTryFromPrimitive for #name {}
})
}
/// Implements `TryFrom<Primitive>` for a `#[repr(Primitive)] enum`.
///
/// Attempting to turn a primitive into an enum with `try_from`.
/// ----------------------------------------------
///
/// ```rust
/// use num_enum::TryFromPrimitive;
/// use std::convert::TryFrom;
///
/// #[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
/// #[repr(u8)]
/// enum Number {
/// Zero,
/// One,
/// }
///
/// let zero = Number::try_from(0u8);
/// assert_eq!(zero, Ok(Number::Zero));
///
/// let three = Number::try_from(3u8);
/// assert_eq!(
/// three.unwrap_err().to_string(),
/// "No discriminant in enum `Number` matches the value `3`",
/// );
/// ```
#[proc_macro_derive(TryFromPrimitive, attributes(num_enum))]
pub fn derive_try_from_primitive(input: TokenStream) -> TokenStream {
let enum_info: EnumInfo = parse_macro_input!(input);
let krate = Ident::new(&get_crate_name(), Span::call_site());
let EnumInfo {
ref name,
ref repr,
ref error_type_info,
..
} = enum_info;
let variant_idents: Vec<Ident> = enum_info.variant_idents();
let expression_idents: Vec<Vec<Ident>> = enum_info.expression_idents();
let variant_expressions: Vec<Vec<Expr>> = enum_info.variant_expressions();
debug_assert_eq!(variant_idents.len(), variant_expressions.len());
let error_type = &error_type_info.name;
let error_constructor = &error_type_info.constructor;
TokenStream::from(quote! {
impl ::#krate::TryFromPrimitive for #name {
type Primitive = #repr;
type Error = #error_type;
const NAME: &'static str = stringify!(#name);
fn try_from_primitive (
number: Self::Primitive,
) -> ::core::result::Result<
Self,
#error_type
> {
// Use intermediate const(s) so that enums defined like
// `Two = ONE + 1u8` work properly.
#![allow(non_upper_case_globals)]
#(
#(
const #expression_idents: #repr = #variant_expressions;
)*
)*
#[deny(unreachable_patterns)]
match number {
#(
#( #expression_idents )|*
=> ::core::result::Result::Ok(Self::#variant_idents),
)*
#[allow(unreachable_patterns)]
_ => ::core::result::Result::Err(
#error_constructor ( number )
),
}
}
}
impl ::core::convert::TryFrom<#repr> for #name {
type Error = #error_type;
#[inline]
fn try_from (
number: #repr,
) -> ::core::result::Result<Self, #error_type>
{
::#krate::TryFromPrimitive::try_from_primitive(number)
}
}
#[doc(hidden)]
impl ::#krate::CannotDeriveBothFromPrimitiveAndTryFromPrimitive for #name {}
})
}
/// Generates a `unsafe fn unchecked_transmute_from(number: Primitive) -> Self`
/// associated function.
///
/// Allows unsafely turning a primitive into an enum with unchecked_transmute_from
/// ------------------------------------------------------------------------------
///
/// If you're really certain a conversion will succeed, and want to avoid a small amount of overhead, you can use unsafe
/// code to do this conversion. Unless you have data showing that the match statement generated in the `try_from` above is a
/// bottleneck for you, you should avoid doing this, as the unsafe code has potential to cause serious memory issues in
/// your program.
///
/// Note that this derive ignores any `default`, `catch_all`, and `alternatives` attributes on the enum.
/// If you need support for conversions from these values, you should use `TryFromPrimitive` or `FromPrimitive`.
///
/// ```rust
/// use num_enum::UnsafeFromPrimitive;
///
/// #[derive(Debug, Eq, PartialEq, UnsafeFromPrimitive)]
/// #[repr(u8)]
/// enum Number {
/// Zero,
/// One,
/// }
///
/// fn main() {
/// assert_eq!(
/// Number::Zero,
/// unsafe { Number::unchecked_transmute_from(0_u8) },
/// );
/// assert_eq!(
/// Number::One,
/// unsafe { Number::unchecked_transmute_from(1_u8) },
/// );
/// }
///
/// unsafe fn undefined_behavior() {
/// let _ = Number::unchecked_transmute_from(2); // 2 is not a valid discriminant!
/// }
/// ```
#[proc_macro_derive(UnsafeFromPrimitive, attributes(num_enum))]
pub fn derive_unsafe_from_primitive(stream: TokenStream) -> TokenStream {
let enum_info = parse_macro_input!(stream as EnumInfo);
let krate = Ident::new(&get_crate_name(), Span::call_site());
let EnumInfo {
ref name, ref repr, ..
} = enum_info;
TokenStream::from(quote! {
impl ::#krate::UnsafeFromPrimitive for #name {
type Primitive = #repr;
unsafe fn unchecked_transmute_from(number: Self::Primitive) -> Self {
::core::mem::transmute(number)
}
}
})
}
/// Implements `core::default::Default` for a `#[repr(Primitive)] enum`.
///
/// Whichever variant has the `#[default]` or `#[num_enum(default)]` attribute will be returned.
/// ----------------------------------------------
///
/// ```rust
/// #[derive(Debug, Eq, PartialEq, num_enum::Default)]
/// #[repr(u8)]
/// enum Number {
/// Zero,
/// #[default]
/// One,
/// }
///
/// assert_eq!(Number::One, Number::default());
/// assert_eq!(Number::One, <Number as ::core::default::Default>::default());
/// ```
#[proc_macro_derive(Default, attributes(num_enum, default))]
pub fn derive_default(stream: TokenStream) -> TokenStream {
let enum_info = parse_macro_input!(stream as EnumInfo);
let default_ident = match enum_info.default() {
Some(ident) => ident,
None => {
let span = Span::call_site();
let message =
"#[derive(num_enum::Default)] requires enum to be exhaustive, or a variant marked with `#[default]` or `#[num_enum(default)]`";
return syn::Error::new(span, message).to_compile_error().into();
}
};
let EnumInfo { ref name, .. } = enum_info;
TokenStream::from(quote! {
impl ::core::default::Default for #name {
#[inline]
fn default() -> Self {
Self::#default_ident
}
}
})
}

554
vendor/num_enum_derive/src/parsing.rs vendored Normal file
View File

@@ -0,0 +1,554 @@
use crate::enum_attributes::ErrorTypeAttribute;
use crate::utils::die;
use crate::variant_attributes::{NumEnumVariantAttributeItem, NumEnumVariantAttributes};
use proc_macro2::Span;
use quote::{format_ident, ToTokens};
use std::collections::BTreeSet;
use syn::{
parse::{Parse, ParseStream},
parse_quote, Attribute, Data, DeriveInput, Expr, ExprLit, ExprUnary, Fields, Ident, Lit,
LitInt, Meta, Path, Result, UnOp,
};
pub(crate) struct EnumInfo {
pub(crate) name: Ident,
pub(crate) repr: Ident,
pub(crate) variants: Vec<VariantInfo>,
pub(crate) error_type_info: ErrorType,
}
impl EnumInfo {
/// Returns whether the number of variants (ignoring defaults, catch-alls, etc) is the same as
/// the capacity of the repr.
pub(crate) fn is_naturally_exhaustive(&self) -> Result<bool> {
let repr_str = self.repr.to_string();
if !repr_str.is_empty() {
let suffix = repr_str
.strip_prefix('i')
.or_else(|| repr_str.strip_prefix('u'));
if let Some(suffix) = suffix {
if suffix == "size" {
return Ok(false);
} else if let Ok(bits) = suffix.parse::<u32>() {
let variants = 1usize.checked_shl(bits);
return Ok(variants.map_or(false, |v| {
v == self
.variants
.iter()
.map(|v| v.alternative_values.len() + 1)
.sum()
}));
}
}
}
die!(self.repr.clone() => "Failed to parse repr into bit size");
}
pub(crate) fn default(&self) -> Option<&Ident> {
self.variants
.iter()
.find(|info| info.is_default)
.map(|info| &info.ident)
}
pub(crate) fn catch_all(&self) -> Option<&Ident> {
self.variants
.iter()
.find(|info| info.is_catch_all)
.map(|info| &info.ident)
}
pub(crate) fn variant_idents(&self) -> Vec<Ident> {
self.variants
.iter()
.filter(|variant| !variant.is_catch_all)
.map(|variant| variant.ident.clone())
.collect()
}
pub(crate) fn expression_idents(&self) -> Vec<Vec<Ident>> {
self.variants
.iter()
.filter(|variant| !variant.is_catch_all)
.map(|info| {
let indices = 0..(info.alternative_values.len() + 1);
indices
.map(|index| format_ident!("{}__num_enum_{}__", info.ident, index))
.collect()
})
.collect()
}
pub(crate) fn variant_expressions(&self) -> Vec<Vec<Expr>> {
self.variants
.iter()
.filter(|variant| !variant.is_catch_all)
.map(|variant| variant.all_values().cloned().collect())
.collect()
}
fn parse_attrs<Attrs: Iterator<Item = Attribute>>(
attrs: Attrs,
) -> Result<(Ident, Option<ErrorType>)> {
let mut maybe_repr = None;
let mut maybe_error_type = None;
for attr in attrs {
if let Meta::List(meta_list) = &attr.meta {
if let Some(ident) = meta_list.path.get_ident() {
if ident == "repr" {
let mut nested = meta_list.tokens.clone().into_iter();
let repr_tree = match (nested.next(), nested.next()) {
(Some(repr_tree), None) => repr_tree,
_ => die!(attr =>
"Expected exactly one `repr` argument"
),
};
let repr_ident: Ident = parse_quote! {
#repr_tree
};
if repr_ident == "C" {
die!(repr_ident =>
"repr(C) doesn't have a well defined size"
);
} else {
maybe_repr = Some(repr_ident);
}
} else if ident == "num_enum" {
let attributes =
attr.parse_args_with(crate::enum_attributes::Attributes::parse)?;
if let Some(error_type) = attributes.error_type {
if maybe_error_type.is_some() {
die!(attr => "At most one num_enum error_type attribute may be specified");
}
maybe_error_type = Some(error_type.into());
}
}
}
}
}
if maybe_repr.is_none() {
die!("Missing `#[repr({Integer})]` attribute");
}
Ok((maybe_repr.unwrap(), maybe_error_type))
}
}
impl Parse for EnumInfo {
fn parse(input: ParseStream) -> Result<Self> {
Ok({
let input: DeriveInput = input.parse()?;
let name = input.ident;
let data = match input.data {
Data::Enum(data) => data,
Data::Union(data) => die!(data.union_token => "Expected enum but found union"),
Data::Struct(data) => die!(data.struct_token => "Expected enum but found struct"),
};
let (repr, maybe_error_type) = Self::parse_attrs(input.attrs.into_iter())?;
let mut variants: Vec<VariantInfo> = vec![];
let mut has_default_variant: bool = false;
let mut has_catch_all_variant: bool = false;
// Vec to keep track of the used discriminants and alt values.
let mut discriminant_int_val_set = BTreeSet::new();
let mut next_discriminant = literal(0);
for variant in data.variants.into_iter() {
let ident = variant.ident.clone();
let discriminant = match &variant.discriminant {
Some(d) => d.1.clone(),
None => next_discriminant.clone(),
};
let mut raw_alternative_values: Vec<Expr> = vec![];
// Keep the attribute around for better error reporting.
let mut alt_attr_ref: Vec<&Attribute> = vec![];
// `#[num_enum(default)]` is required by `#[derive(FromPrimitive)]`
// and forbidden by `#[derive(UnsafeFromPrimitive)]`, so we need to
// keep track of whether we encountered such an attribute:
let mut is_default: bool = false;
let mut is_catch_all: bool = false;
for attribute in &variant.attrs {
if attribute.path().is_ident("default") {
if has_default_variant {
die!(attribute =>
"Multiple variants marked `#[default]` or `#[num_enum(default)]` found"
);
} else if has_catch_all_variant {
die!(attribute =>
"Attribute `default` is mutually exclusive with `catch_all`"
);
}
is_default = true;
has_default_variant = true;
}
if attribute.path().is_ident("num_enum") {
match attribute.parse_args_with(NumEnumVariantAttributes::parse) {
Ok(variant_attributes) => {
for variant_attribute in variant_attributes.items {
match variant_attribute {
NumEnumVariantAttributeItem::Default(default) => {
if has_default_variant {
die!(default.keyword =>
"Multiple variants marked `#[default]` or `#[num_enum(default)]` found"
);
} else if has_catch_all_variant {
die!(default.keyword =>
"Attribute `default` is mutually exclusive with `catch_all`"
);
}
is_default = true;
has_default_variant = true;
}
NumEnumVariantAttributeItem::CatchAll(catch_all) => {
if has_catch_all_variant {
die!(catch_all.keyword =>
"Multiple variants marked with `#[num_enum(catch_all)]`"
);
} else if has_default_variant {
die!(catch_all.keyword =>
"Attribute `catch_all` is mutually exclusive with `default`"
);
}
match variant
.fields
.iter()
.collect::<Vec<_>>()
.as_slice()
{
[syn::Field {
ty: syn::Type::Path(syn::TypePath { path, .. }),
..
}] if path.is_ident(&repr) => {
is_catch_all = true;
has_catch_all_variant = true;
}
_ => {
die!(catch_all.keyword =>
"Variant with `catch_all` must be a tuple with exactly 1 field matching the repr type"
);
}
}
}
NumEnumVariantAttributeItem::Alternatives(alternatives) => {
raw_alternative_values.extend(alternatives.expressions);
alt_attr_ref.push(attribute);
}
}
}
}
Err(err) => {
if cfg!(not(feature = "complex-expressions")) {
let tokens = attribute.meta.to_token_stream();
let attribute_str = format!("{}", tokens);
if attribute_str.contains("alternatives")
&& attribute_str.contains("..")
{
// Give a nice error message suggesting how to fix the problem.
die!(attribute => "Ranges are only supported as num_enum alternate values if the `complex-expressions` feature of the crate `num_enum` is enabled".to_string())
}
}
die!(attribute =>
format!("Invalid attribute: {}", err)
);
}
}
}
}
if !is_catch_all {
match &variant.fields {
Fields::Named(_) | Fields::Unnamed(_) => {
die!(variant => format!("`{}` only supports unit variants (with no associated data), but `{}::{}` was not a unit variant.", get_crate_name(), name, ident));
}
Fields::Unit => {}
}
}
let discriminant_value = parse_discriminant(&discriminant)?;
// Check for collision.
// We can't do const evaluation, or even compare arbitrary Exprs,
// so unfortunately we can't check for duplicates.
// That's not the end of the world, just we'll end up with compile errors for
// matches with duplicate branches in generated code instead of nice friendly error messages.
if let DiscriminantValue::Literal(canonical_value_int) = discriminant_value {
if discriminant_int_val_set.contains(&canonical_value_int) {
die!(ident => format!("The discriminant '{}' collides with a value attributed to a previous variant", canonical_value_int))
}
}
// Deal with the alternative values.
let mut flattened_alternative_values = Vec::new();
let mut flattened_raw_alternative_values = Vec::new();
for raw_alternative_value in raw_alternative_values {
let expanded_values = parse_alternative_values(&raw_alternative_value)?;
for expanded_value in expanded_values {
flattened_alternative_values.push(expanded_value);
flattened_raw_alternative_values.push(raw_alternative_value.clone())
}
}
if !flattened_alternative_values.is_empty() {
let alternate_int_values = flattened_alternative_values
.into_iter()
.map(|v| {
match v {
DiscriminantValue::Literal(value) => Ok(value),
DiscriminantValue::Expr(expr) => {
if let Expr::Range(_) = expr {
if cfg!(not(feature = "complex-expressions")) {
// Give a nice error message suggesting how to fix the problem.
die!(expr => "Ranges are only supported as num_enum alternate values if the `complex-expressions` feature of the crate `num_enum` is enabled".to_string())
}
}
// We can't do uniqueness checking on non-literals, so we don't allow them as alternate values.
// We could probably allow them, but there doesn't seem to be much of a use-case,
// and it's easier to give good error messages about duplicate values this way,
// rather than rustc errors on conflicting match branches.
die!(expr => "Only literals are allowed as num_enum alternate values".to_string())
},
}
})
.collect::<Result<Vec<i128>>>()?;
let mut sorted_alternate_int_values = alternate_int_values.clone();
sorted_alternate_int_values.sort_unstable();
let sorted_alternate_int_values = sorted_alternate_int_values;
// Check if the current discriminant is not in the alternative values.
if let DiscriminantValue::Literal(canonical_value_int) = discriminant_value {
if let Some(index) = alternate_int_values
.iter()
.position(|&x| x == canonical_value_int)
{
die!(&flattened_raw_alternative_values[index] => format!("'{}' in the alternative values is already attributed as the discriminant of this variant", canonical_value_int));
}
}
// Search for duplicates, the vec is sorted. Warn about them.
if (1..sorted_alternate_int_values.len()).any(|i| {
sorted_alternate_int_values[i] == sorted_alternate_int_values[i - 1]
}) {
let attr = *alt_attr_ref.last().unwrap();
die!(attr => "There is duplication in the alternative values");
}
// Search if those discriminant_int_val_set where already attributed.
// (discriminant_int_val_set is BTreeSet, and iter().next_back() is the is the maximum in the set.)
if let Some(last_upper_val) = discriminant_int_val_set.iter().next_back() {
if sorted_alternate_int_values.first().unwrap() <= last_upper_val {
for (index, val) in alternate_int_values.iter().enumerate() {
if discriminant_int_val_set.contains(val) {
die!(&flattened_raw_alternative_values[index] => format!("'{}' in the alternative values is already attributed to a previous variant", val));
}
}
}
}
// Reconstruct the alternative_values vec of Expr but sorted.
flattened_raw_alternative_values = sorted_alternate_int_values
.iter()
.map(|val| literal(val.to_owned()))
.collect();
// Add the alternative values to the the set to keep track.
discriminant_int_val_set.extend(sorted_alternate_int_values);
}
// Add the current discriminant to the the set to keep track.
if let DiscriminantValue::Literal(canonical_value_int) = discriminant_value {
discriminant_int_val_set.insert(canonical_value_int);
}
variants.push(VariantInfo {
ident,
is_default,
is_catch_all,
canonical_value: discriminant,
alternative_values: flattened_raw_alternative_values,
});
// Get the next value for the discriminant.
next_discriminant = match discriminant_value {
DiscriminantValue::Literal(int_value) => literal(int_value.wrapping_add(1)),
DiscriminantValue::Expr(expr) => {
parse_quote! {
#repr::wrapping_add(#expr, 1)
}
}
}
}
let error_type_info = maybe_error_type.unwrap_or_else(|| {
let crate_name = Ident::new(&get_crate_name(), Span::call_site());
ErrorType {
name: parse_quote! {
::#crate_name::TryFromPrimitiveError<Self>
},
constructor: parse_quote! {
::#crate_name::TryFromPrimitiveError::<Self>::new
},
}
});
EnumInfo {
name,
repr,
variants,
error_type_info,
}
})
}
}
fn literal(i: i128) -> Expr {
Expr::Lit(ExprLit {
lit: Lit::Int(LitInt::new(&i.to_string(), Span::call_site())),
attrs: vec![],
})
}
enum DiscriminantValue {
Literal(i128),
Expr(Expr),
}
fn parse_discriminant(val_exp: &Expr) -> Result<DiscriminantValue> {
let mut sign = 1;
let mut unsigned_expr = val_exp;
if let Expr::Unary(ExprUnary {
op: UnOp::Neg(..),
expr,
..
}) = val_exp
{
unsigned_expr = expr;
sign = -1;
}
if let Expr::Lit(ExprLit {
lit: Lit::Int(ref lit_int),
..
}) = unsigned_expr
{
Ok(DiscriminantValue::Literal(
sign * lit_int.base10_parse::<i128>()?,
))
} else {
Ok(DiscriminantValue::Expr(val_exp.clone()))
}
}
#[cfg(feature = "complex-expressions")]
fn parse_alternative_values(val_expr: &Expr) -> Result<Vec<DiscriminantValue>> {
fn range_expr_value_to_number(
parent_range_expr: &Expr,
range_bound_value: &Option<Box<Expr>>,
) -> Result<i128> {
// Avoid needing to calculate what the lower and upper bound would be - these are type dependent,
// and also may not be obvious in context (e.g. an omitted bound could reasonably mean "from the last discriminant" or "from the lower bound of the type").
if let Some(range_bound_value) = range_bound_value {
let range_bound_value = parse_discriminant(range_bound_value.as_ref())?;
// If non-literals are used, we can't expand to the mapped values, so can't write a nice match statement or do exhaustiveness checking.
// Require literals instead.
if let DiscriminantValue::Literal(value) = range_bound_value {
return Ok(value);
}
}
die!(parent_range_expr => "When ranges are used for alternate values, both bounds most be explicitly specified numeric literals")
}
if let Expr::Range(syn::ExprRange {
start, end, limits, ..
}) = val_expr
{
let lower = range_expr_value_to_number(val_expr, start)?;
let upper = range_expr_value_to_number(val_expr, end)?;
// While this is technically allowed in Rust, and results in an empty range, it's almost certainly a mistake in this context.
if lower > upper {
die!(val_expr => "When using ranges for alternate values, upper bound must not be less than lower bound");
}
let mut values = Vec::with_capacity((upper - lower) as usize);
let mut next = lower;
loop {
match limits {
syn::RangeLimits::HalfOpen(..) => {
if next == upper {
break;
}
}
syn::RangeLimits::Closed(..) => {
if next > upper {
break;
}
}
}
values.push(DiscriminantValue::Literal(next));
next += 1;
}
return Ok(values);
}
parse_discriminant(val_expr).map(|v| vec![v])
}
#[cfg(not(feature = "complex-expressions"))]
fn parse_alternative_values(val_expr: &Expr) -> Result<Vec<DiscriminantValue>> {
parse_discriminant(val_expr).map(|v| vec![v])
}
pub(crate) struct VariantInfo {
ident: Ident,
is_default: bool,
is_catch_all: bool,
canonical_value: Expr,
alternative_values: Vec<Expr>,
}
impl VariantInfo {
fn all_values(&self) -> impl Iterator<Item = &Expr> {
::core::iter::once(&self.canonical_value).chain(self.alternative_values.iter())
}
}
pub(crate) struct ErrorType {
pub(crate) name: Path,
pub(crate) constructor: Path,
}
impl From<ErrorTypeAttribute> for ErrorType {
fn from(attribute: ErrorTypeAttribute) -> Self {
Self {
name: attribute.name.path,
constructor: attribute.constructor.path,
}
}
}
#[cfg(feature = "proc-macro-crate")]
pub(crate) fn get_crate_name() -> String {
let found_crate = proc_macro_crate::crate_name("num_enum").unwrap_or_else(|err| {
eprintln!("Warning: {}\n => defaulting to `num_enum`", err,);
proc_macro_crate::FoundCrate::Itself
});
match found_crate {
proc_macro_crate::FoundCrate::Itself => String::from("num_enum"),
proc_macro_crate::FoundCrate::Name(name) => name,
}
}
// Don't depend on proc-macro-crate in no_std environments because it causes an awkward dependency
// on serde with std.
//
// no_std dependees on num_enum cannot rename the num_enum crate when they depend on it. Sorry.
//
// See https://github.com/illicitonion/num_enum/issues/18
#[cfg(not(feature = "proc-macro-crate"))]
pub(crate) fn get_crate_name() -> String {
String::from("num_enum")
}

15
vendor/num_enum_derive/src/utils.rs vendored Normal file
View File

@@ -0,0 +1,15 @@
macro_rules! die {
($spanned:expr=>
$msg:expr
) => {
return Err(::syn::Error::new_spanned($spanned, $msg))
};
(
$msg:expr
) => {
return Err(::syn::Error::new(::proc_macro2::Span::call_site(), $msg))
};
}
pub(crate) use die;

View File

@@ -0,0 +1,90 @@
use syn::{
parse::{Parse, ParseStream},
Expr, Result,
};
mod kw {
syn::custom_keyword!(default);
syn::custom_keyword!(catch_all);
syn::custom_keyword!(alternatives);
}
pub(crate) struct NumEnumVariantAttributes {
pub(crate) items: syn::punctuated::Punctuated<NumEnumVariantAttributeItem, syn::Token![,]>,
}
impl Parse for NumEnumVariantAttributes {
fn parse(input: ParseStream<'_>) -> Result<Self> {
Ok(Self {
items: input.parse_terminated(NumEnumVariantAttributeItem::parse, syn::Token![,])?,
})
}
}
pub(crate) enum NumEnumVariantAttributeItem {
Default(VariantDefaultAttribute),
CatchAll(VariantCatchAllAttribute),
Alternatives(VariantAlternativesAttribute),
}
impl Parse for NumEnumVariantAttributeItem {
fn parse(input: ParseStream<'_>) -> Result<Self> {
let lookahead = input.lookahead1();
if lookahead.peek(kw::default) {
input.parse().map(Self::Default)
} else if lookahead.peek(kw::catch_all) {
input.parse().map(Self::CatchAll)
} else if lookahead.peek(kw::alternatives) {
input.parse().map(Self::Alternatives)
} else {
Err(lookahead.error())
}
}
}
pub(crate) struct VariantDefaultAttribute {
pub(crate) keyword: kw::default,
}
impl Parse for VariantDefaultAttribute {
fn parse(input: ParseStream) -> Result<Self> {
Ok(Self {
keyword: input.parse()?,
})
}
}
pub(crate) struct VariantCatchAllAttribute {
pub(crate) keyword: kw::catch_all,
}
impl Parse for VariantCatchAllAttribute {
fn parse(input: ParseStream) -> Result<Self> {
Ok(Self {
keyword: input.parse()?,
})
}
}
pub(crate) struct VariantAlternativesAttribute {
_keyword: kw::alternatives,
_eq_token: syn::Token![=],
_bracket_token: syn::token::Bracket,
pub(crate) expressions: syn::punctuated::Punctuated<Expr, syn::Token![,]>,
}
impl Parse for VariantAlternativesAttribute {
fn parse(input: ParseStream) -> Result<Self> {
let content;
let keyword = input.parse()?;
let _eq_token = input.parse()?;
let _bracket_token = syn::bracketed!(content in input);
let expressions = content.parse_terminated(Expr::parse, syn::Token![,])?;
Ok(Self {
_keyword: keyword,
_eq_token,
_bracket_token,
expressions,
})
}
}