diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 648a1fc..8a877f8 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -5,8 +5,8 @@ extern crate proc_macro; mod traits; use proc_macro2::TokenStream; -use quote::{quote, quote_spanned}; -use syn::{parse_macro_input, DeriveInput, spanned::Spanned}; +use quote::quote; +use syn::{parse_macro_input, DeriveInput, Result}; use crate::traits::{ AnyBitPattern, Contiguous, Derivable, CheckedBitPattern, NoUninit, Pod, TransparentWrapper, Zeroable, @@ -223,17 +223,13 @@ pub fn derive_contiguous( /// Basic wrapper for error handling fn derive_marker_trait(input: DeriveInput) -> TokenStream { - let span = input.span(); - derive_marker_trait_inner::(input).unwrap_or_else(|err| { - quote_spanned! { span => - compile_error!(#err); - } - }) + derive_marker_trait_inner::(input) + .unwrap_or_else(|err| err.into_compile_error()) } fn derive_marker_trait_inner( input: DeriveInput, -) -> Result { +) -> Result { let name = &input.ident; let (impl_generics, ty_generics, where_clause) = diff --git a/derive/src/traits.rs b/derive/src/traits.rs index 0230ac9..455d1ef 100644 --- a/derive/src/traits.rs +++ b/derive/src/traits.rs @@ -1,30 +1,42 @@ +#![allow(unused_imports)] use proc_macro2::{Ident, Span, TokenStream, TokenTree}; use quote::{quote, quote_spanned, ToTokens}; -use syn::{ - spanned::Spanned, AttrStyle, Attribute, Data, DataEnum, DataStruct, - DataUnion, DeriveInput, Expr, ExprLit, ExprUnary, Fields, Lit, LitInt, Meta, - NestedMeta, Type, UnOp, Variant, +use syn::{*, + parse::{Parse, Parser, ParseStream}, + punctuated::Punctuated, + spanned::Spanned, + Result, }; +macro_rules! bail { + ($msg:expr $(,)?) => ( + return Err(Error::new(Span::call_site(), &$msg[..])) + ); + + ( $msg:expr => $span_to_blame:expr $(,)? ) => ( + return Err(Error::new_spanned(&$span_to_blame, $msg)) + ); +} + pub trait Derivable { fn ident() -> TokenStream; fn implies_trait() -> Option { None } - fn generic_params(_input: &DeriveInput) -> Result { + fn generic_params(_input: &DeriveInput) -> Result { Ok(quote!()) } - fn asserts(_input: &DeriveInput) -> Result { + fn asserts(_input: &DeriveInput) -> Result { Ok(quote!()) } fn check_attributes( _ty: &Data, _attributes: &[Attribute], - ) -> Result<(), &'static str> { + ) -> Result<()> { Ok(()) } fn trait_impl( _input: &DeriveInput, - ) -> Result<(TokenStream, TokenStream), &'static str> { + ) -> Result<(TokenStream, TokenStream)> { Ok((quote!(), quote!())) } } @@ -36,13 +48,17 @@ impl Derivable for Pod { quote!(::bytemuck::Pod) } - fn asserts(input: &DeriveInput) -> Result { - let repr = get_repr(&input.attrs); + fn asserts(input: &DeriveInput) -> Result { + let repr = get_repr(&input.attrs)?; let completly_packed = repr.packed == Some(1); if !completly_packed && !input.generics.params.is_empty() { - return Err("Pod requires cannot be derived for non-packed types containing generic parameters because the padding requirements can't be verified for generic non-packed structs"); + bail!("\ + Pod requires cannot be derived for non-packed types containing \ + generic parameters because the padding requirements can't be verified \ + for generic non-packed structs\ + " => input.generics.params.first().unwrap()); } match &input.data { @@ -60,20 +76,20 @@ impl Derivable for Pod { #assert_fields_are_pod )) } - Data::Enum(_) => Err("Deriving Pod is not supported for enums"), - Data::Union(_) => Err("Deriving Pod is not supported for unions"), + Data::Enum(_) => bail!("Deriving Pod is not supported for enums"), + Data::Union(_) => bail!("Deriving Pod is not supported for unions"), } } fn check_attributes( _ty: &Data, attributes: &[Attribute], - ) -> Result<(), &'static str> { - let repr = get_repr(attributes); + ) -> Result<()> { + let repr = get_repr(attributes)?; match repr.repr { Repr::C => Ok(()), Repr::Transparent => Ok(()), _ => { - Err("Pod requires the type to be #[repr(C)] or #[repr(transparent)]") + bail!("Pod requires the type to be #[repr(C)] or #[repr(transparent)]") } } } @@ -90,11 +106,11 @@ impl Derivable for AnyBitPattern { Some(quote!(::bytemuck::Zeroable)) } - fn asserts(input: &DeriveInput) -> Result { + fn asserts(input: &DeriveInput) -> Result { match &input.data { Data::Union(_) => Ok(quote!()), // unions are always `AnyBitPattern` Data::Struct(_) => generate_fields_are_trait(input, Self::ident()), - Data::Enum(_) => Err("Deriving AnyBitPattern is not supported for enums"), + Data::Enum(_) => bail!("Deriving AnyBitPattern is not supported for enums"), } } } @@ -106,11 +122,11 @@ impl Derivable for Zeroable { quote!(::bytemuck::Zeroable) } - fn asserts(input: &DeriveInput) -> Result { + fn asserts(input: &DeriveInput) -> Result { match &input.data { Data::Union(_) => Ok(quote!()), // unions are always `Zeroable` Data::Struct(_) => generate_fields_are_trait(input, Self::ident()), - Data::Enum(_) => Err("Deriving Zeroable is not supported for enums"), + Data::Enum(_) => bail!("Deriving Zeroable is not supported for enums"), } } } @@ -124,25 +140,25 @@ impl Derivable for NoUninit { fn check_attributes( ty: &Data, attributes: &[Attribute], - ) -> Result<(), &'static str> { - let repr = get_repr(attributes); + ) -> Result<()> { + let repr = get_repr(attributes)?; match ty { Data::Struct(_) => match repr.repr { Repr::C | Repr::Transparent => Ok(()), - _ => Err("NoUninit requires the struct to be #[repr(C)] or #[repr(transparent)]"), + _ => bail!("NoUninit requires the struct to be #[repr(C)] or #[repr(transparent)]"), }, Data::Enum(_) => if repr.repr.is_integer() { Ok(()) } else { - Err("NoUninit requires the enum to be an explicit #[repr(Int)]") + bail!("NoUninit requires the enum to be an explicit #[repr(Int)]") }, - Data::Union(_) => Err("NoUninit can only be derived on enums and structs") + Data::Union(_) => bail!("NoUninit can only be derived on enums and structs") } } - fn asserts(input: &DeriveInput) -> Result { + fn asserts(input: &DeriveInput) -> Result { if !input.generics.params.is_empty() { - return Err("NoUninit cannot be derived for structs containing generic parameters because the padding requirements can't be verified for generic structs"); + bail!("NoUninit cannot be derived for structs containing generic parameters because the padding requirements can't be verified for generic structs"); } match &input.data { @@ -158,18 +174,18 @@ impl Derivable for NoUninit { } Data::Enum(DataEnum { variants, .. }) => { if variants.iter().any(|variant| !variant.fields.is_empty()) { - Err("Only fieldless enums are supported for NoUninit") + bail!("Only fieldless enums are supported for NoUninit") } else { Ok(quote!()) } } - Data::Union(_) => Err("NoUninit cannot be derived for unions"), // shouldn't be possible since we already error in attribute check for this case + Data::Union(_) => bail!("NoUninit cannot be derived for unions"), // shouldn't be possible since we already error in attribute check for this case } } fn trait_impl( _input: &DeriveInput, - ) -> Result<(TokenStream, TokenStream), &'static str> { + ) -> Result<(TokenStream, TokenStream)> { Ok((quote!(), quote!())) } } @@ -183,25 +199,25 @@ impl Derivable for CheckedBitPattern { fn check_attributes( ty: &Data, attributes: &[Attribute], - ) -> Result<(), &'static str> { - let repr = get_repr(attributes); + ) -> Result<()> { + let repr = get_repr(attributes)?; match ty { Data::Struct(_) => match repr.repr { Repr::C | Repr::Transparent => Ok(()), - _ => Err("CheckedBitPattern derive requires the struct to be #[repr(C)] or #[repr(transparent)]"), + _ => bail!("CheckedBitPattern derive requires the struct to be #[repr(C)] or #[repr(transparent)]"), }, Data::Enum(_) => if repr.repr.is_integer() { Ok(()) } else { - Err("CheckedBitPattern requires the enum to be an explicit #[repr(Int)]") + bail!("CheckedBitPattern requires the enum to be an explicit #[repr(Int)]") }, - Data::Union(_) => Err("CheckedBitPattern can only be derived on enums and structs") + Data::Union(_) => bail!("CheckedBitPattern can only be derived on enums and structs") } } - fn asserts(input: &DeriveInput) -> Result { + fn asserts(input: &DeriveInput) -> Result { if !input.generics.params.is_empty() { - return Err("CheckedBitPattern cannot be derived for structs containing generic parameters"); + bail!("CheckedBitPattern cannot be derived for structs containing generic parameters"); } match &input.data { @@ -212,19 +228,19 @@ impl Derivable for CheckedBitPattern { Ok(assert_fields_are_maybe_pod) } Data::Enum(_) => Ok(quote!()), // nothing needed, already guaranteed OK by NoUninit - Data::Union(_) => Err("Internal error in CheckedBitPattern derive"), // shouldn't be possible since we already error in attribute check for this case + Data::Union(_) => bail!("Internal error in CheckedBitPattern derive"), // shouldn't be possible since we already error in attribute check for this case } } fn trait_impl( input: &DeriveInput, - ) -> Result<(TokenStream, TokenStream), &'static str> { + ) -> Result<(TokenStream, TokenStream)> { match &input.data { - Data::Struct(DataStruct { fields, .. }) => Ok( - generate_checked_bit_pattern_struct(&input.ident, fields, &input.attrs), - ), + Data::Struct(DataStruct { fields, .. }) => { + generate_checked_bit_pattern_struct(&input.ident, fields, &input.attrs) + }, Data::Enum(_) => generate_checked_bit_pattern_enum(input), - Data::Union(_) => Err("Internal error in CheckedBitPattern derive"), // shouldn't be possible since we already error in attribute check for this case + Data::Union(_) => bail!("Internal error in CheckedBitPattern derive"), // shouldn't be possible since we already error in attribute check for this case } } } @@ -254,14 +270,19 @@ impl Derivable for TransparentWrapper { quote!(::bytemuck::TransparentWrapper) } - fn generic_params(input: &DeriveInput) -> Result { + fn generic_params(input: &DeriveInput) -> Result { let fields = get_struct_fields(input)?; - Self::get_wrapper_type(&input.attrs, &fields).map(|ty| quote!(<#ty>)) - .ok_or("when deriving TransparentWrapper for a struct with more than one field you need to specify the transparent field using #[transparent(T)]") + match Self::get_wrapper_type(&input.attrs, &fields) { + | Some(ty) => Ok(quote!(<#ty>)), + | None => bail!("\ + when deriving TransparentWrapper for a struct with more than one field \ + you need to specify the transparent field using #[transparent(T)]\ + "), + } } - fn asserts(input: &DeriveInput) -> Result { + fn asserts(input: &DeriveInput) -> Result { let fields = get_struct_fields(input)?; let wrapped_type = match Self::get_wrapper_type(&input.attrs, &fields) { Some(wrapped_type) => wrapped_type.to_string(), @@ -271,10 +292,10 @@ impl Derivable for TransparentWrapper { .iter() .filter(|field| field.ty.to_token_stream().to_string() == wrapped_type); if let None = wrapped_fields.next() { - return Err("TransparentWrapper must have one field of the wrapped type"); + bail!("TransparentWrapper must have one field of the wrapped type"); }; if let Some(_) = wrapped_fields.next() { - Err("TransparentWrapper can only have one field of the wrapped type") + bail!("TransparentWrapper can only have one field of the wrapped type") } else { Ok(quote!()) } @@ -282,13 +303,13 @@ impl Derivable for TransparentWrapper { fn check_attributes( _ty: &Data, attributes: &[Attribute], - ) -> Result<(), &'static str> { - let repr = get_repr(attributes); + ) -> Result<()> { + let repr = get_repr(attributes)?; match repr.repr { Repr::Transparent => Ok(()), _ => { - Err("TransparentWrapper requires the struct to be #[repr(transparent)]") + bail!("TransparentWrapper requires the struct to be #[repr(transparent)]") } } } @@ -303,13 +324,13 @@ impl Derivable for Contiguous { fn trait_impl( input: &DeriveInput, - ) -> Result<(TokenStream, TokenStream), &'static str> { - let repr = get_repr(&input.attrs); + ) -> Result<(TokenStream, TokenStream)> { + let repr = get_repr(&input.attrs)?; let integer_ty = if let Some(integer_ty) = repr.repr.as_integer_type() { integer_ty } else { - return Err("Contiguous requires the enum to be #[repr(Int)]"); + bail!("Contiguous requires the enum to be #[repr(Int)]"); }; let variants = get_enum_variants(input)?; @@ -320,7 +341,7 @@ impl Derivable for Contiguous { (i64::max_value(), i64::min_value(), 0), |(min, max, count), res| { let discriminator = res?; - Ok(( + Ok::<_, Error>(( i64::min(min, discriminator), i64::max(max, discriminator), count + 1, @@ -329,9 +350,9 @@ impl Derivable for Contiguous { )?; if max - min != count - 1 { - return Err( + bail! { "Contiguous requires the enum discriminants to be contiguous", - ); + } } let min_lit = LitInt::new(&format!("{}", min), input.span()); @@ -348,29 +369,29 @@ impl Derivable for Contiguous { } } -fn get_struct_fields(input: &DeriveInput) -> Result<&Fields, &'static str> { +fn get_struct_fields(input: &DeriveInput) -> Result<&Fields> { if let Data::Struct(DataStruct { fields, .. }) = &input.data { Ok(fields) } else { - Err("deriving this trait is only supported for structs") + bail!("deriving this trait is only supported for structs") } } -fn get_fields(input: &DeriveInput) -> Result { +fn get_fields(input: &DeriveInput) -> Result { match &input.data { Data::Struct(DataStruct { fields, .. }) => Ok(fields.clone()), Data::Union(DataUnion { fields, .. }) => Ok(Fields::Named(fields.clone())), - Data::Enum(_) => Err("deriving this trait is not supported for enums"), + Data::Enum(_) => bail!("deriving this trait is not supported for enums"), } } fn get_enum_variants<'a>( input: &'a DeriveInput, -) -> Result + 'a, &'static str> { +) -> Result + 'a> { if let Data::Enum(DataEnum { variants, .. }) = &input.data { Ok(variants.iter()) } else { - Err("deriving this trait is only supported for enums") + bail!("deriving this trait is only supported for enums") } } @@ -382,10 +403,10 @@ fn get_field_types<'a>( fn generate_checked_bit_pattern_struct( input_ident: &Ident, fields: &Fields, attrs: &[Attribute], -) -> (TokenStream, TokenStream) { +) -> Result<(TokenStream, TokenStream)> { let bits_ty = Ident::new(&format!("{}Bits", input_ident), input_ident.span()); - let repr = get_repr(attrs); + let repr = get_repr(attrs)?; let field_names = fields .iter() @@ -401,12 +422,9 @@ fn generate_checked_bit_pattern_struct( let field_name = &field_names[..]; let field_ty = &field_tys[..]; - #[cfg(not(target_arch = "spirv"))] - let derive_dbg = quote!(#[derive(Debug)]); - #[cfg(target_arch = "spirv")] - let derive_dbg = quote!(); + let derive_dbg = quote!(#[cfg_attr(not(target_arch = "spirv"), derive(Debug))]); - ( + Ok(( quote! { #repr #[derive(Clone, Copy, ::bytemuck::AnyBitPattern)] @@ -424,12 +442,12 @@ fn generate_checked_bit_pattern_struct( #(<#field_ty as ::bytemuck::CheckedBitPattern>::is_valid_bit_pattern(&bits.#field_name) && )* true } }, - ) + )) } fn generate_checked_bit_pattern_enum( input: &DeriveInput, -) -> Result<(TokenStream, TokenStream), &'static str> { +) -> Result<(TokenStream, TokenStream)> { let span = input.span(); let mut variants_with_discriminant = VariantDiscriminantIterator::new(get_enum_variants(input)?); @@ -438,7 +456,7 @@ fn generate_checked_bit_pattern_enum( (i64::max_value(), i64::min_value(), 0), |(min, max, count), res| { let discriminant = res?; - Ok((i64::min(min, discriminant), i64::max(max, discriminant), count + 1)) + Ok::<_, Error>((i64::min(min, discriminant), i64::max(max, discriminant), count + 1)) }, )?; @@ -458,7 +476,7 @@ fn generate_checked_bit_pattern_enum( let variant = res?; Ok(LitInt::new(&format!("{}", variant), span)) }) - .collect::, _>>()?; + .collect::>>()?; // count is at least 1 let first = &variant_lits[0]; @@ -467,7 +485,7 @@ fn generate_checked_bit_pattern_enum( quote!(matches!(*bits, #first #(| #rest )*)) }; - let repr = get_repr(&input.attrs); + let repr = get_repr(&input.attrs)?; let integer_ty = repr.repr.as_integer_type().unwrap(); // should be checked in attr check already Ok(( quote!(), @@ -487,7 +505,7 @@ fn generate_checked_bit_pattern_enum( /// is equal to the sum of the size of it's fields fn generate_assert_no_padding( input: &DeriveInput, -) -> Result { +) -> Result { let struct_type = &input.ident; let span = input.ident.span(); let fields = get_fields(input)?; @@ -512,7 +530,7 @@ fn generate_assert_no_padding( /// Check that all fields implement a given trait fn generate_fields_are_trait( input: &DeriveInput, trait_: TokenStream, -) -> Result { +) -> Result { let (impl_generics, _ty_generics, where_clause) = input.generics.split_for_impl(); let fields = get_fields(input)?; @@ -553,200 +571,143 @@ fn get_simple_attr(attributes: &[Attribute], attr_name: &str) -> Option { None } -#[derive(Clone, Copy)] -struct Representation { - packed: Option, - repr: Repr, -} - -impl ToTokens for Representation { - fn to_tokens(&self, tokens: &mut TokenStream) { - let repr = match self.repr { - Repr::Rust => None, - Repr::C => Some("C"), - Repr::Transparent => Some("transparent"), - Repr::U8 => Some("u8"), - Repr::I8 => Some("i8"), - Repr::U16 => Some("u16"), - Repr::I16 => Some("i16"), - Repr::U32 => Some("u32"), - Repr::I32 => Some("i32"), - Repr::U64 => Some("u64"), - Repr::I64 => Some("i64"), - Repr::I128 => Some("i128"), - Repr::U128 => Some("u128"), - }; - if let Some(repr) = repr { - let ident = Ident::new(repr, Span::call_site()); - tokens.extend(quote! { - #[repr(#ident)] - }); - } - - if let Some(packed) = self.packed { - let lit = LitInt::new(&packed.to_string(), Span::call_site()); - tokens.extend(quote! { - #[repr(packed(#lit))] - }); - } - } -} - -#[derive(Clone, Copy)] -enum Repr { - Rust, - C, - Transparent, - U8, - I8, - U16, - I16, - U32, - I32, - U64, - I64, - I128, - U128, -} - -impl Repr { - fn is_integer(&self) -> bool { - match *self { - Repr::Rust | Repr::C | Repr::Transparent => false, - Repr::U8 - | Repr::I8 - | Repr::U16 - | Repr::I16 - | Repr::U32 - | Repr::I32 - | Repr::U64 - | Repr::I64 - | Repr::I128 - | Repr::U128 => true, - } - } - - fn as_integer_type(&self) -> Option { - match self { - Repr::Rust | Repr::C | Repr::Transparent => None, - Repr::U8 => Some(quote! { ::core::primitive::u8 }), - Repr::I8 => Some(quote! { ::core::primitive::i8 }), - Repr::U16 => Some(quote! { ::core::primitive::u16 }), - Repr::I16 => Some(quote! { ::core::primitive::i16 }), - Repr::U32 => Some(quote! { ::core::primitive::u32 }), - Repr::I32 => Some(quote! { ::core::primitive::i32 }), - Repr::U64 => Some(quote! { ::core::primitive::u64 }), - Repr::I64 => Some(quote! { ::core::primitive::i64 }), - Repr::I128 => Some(quote! { ::core::primitive::u128 }), - Repr::U128 => Some(quote! { ::core::primitive::i128 }), - } - } -} - -fn get_repr(attributes: &[Attribute]) -> Representation { - let mut repr = Representation { packed: None, repr: Repr::Rust }; - - for attr in attributes { - let meta = if let Ok(meta) = attr.parse_meta() { meta } else { continue }; - if !meta.path().is_ident("repr") { - continue; - } - let list = if let Meta::List(list) = meta { - list +fn get_repr(attributes: &[Attribute]) -> Result { + attributes + .iter() + .filter_map(|attr| if attr.path.is_ident("repr") { + Some(attr.parse_args::()) } else { - // The other `Meta` variants are illegal for `repr`. - continue; - }; + None + }) + .try_fold(Representation::default(), |a, b| { + let b = b?; + Ok(Representation { + repr: match (a.repr, b.repr) { + | (a, Repr::Rust) => a, + | (Repr::Rust, b) => b, + | _ => bail!("conflicting representation hints"), + }, + packed: match (a.packed, b.packed) { + | (a, None) => a, + | (None, b) => b, + | _ => bail!("conflicting representation hints"), + }, + }) + }) +} - for item in list.nested { - let meta = if let NestedMeta::Meta(meta) = item { - meta - } else { - // Other nested items are illegal for `repr`. - continue; - }; +mk_repr! { + U8 => u8, + I8 => i8, + U16 => u16, + I16 => i16, + U32 => u32, + I32 => i32, + U64 => u64, + I64 => i64, + I128 => i128, + U128 => u128, +} +// where +macro_rules! mk_repr {( + $( + $Xn:ident => $xn:ident + ),* $(,)? +) => ( + #[derive(Clone, Copy, PartialEq)] + enum Repr { + Rust, + C, + Transparent, + $($Xn),* + } - match meta.path() { - path if path.is_ident("C") => { - repr.repr = Repr::C; - } - path if path.is_ident("transparent") => { - repr.repr = Repr::Transparent; - } - path if path.is_ident("u8") => { - repr.repr = Repr::U8; - } - path if path.is_ident("i8") => { - repr.repr = Repr::I8; - } - path if path.is_ident("u16") => { - repr.repr = Repr::U16; - } - path if path.is_ident("i16") => { - repr.repr = Repr::I16; - } - path if path.is_ident("u32") => { - repr.repr = Repr::U32; - } - path if path.is_ident("i32") => { - repr.repr = Repr::I32; - } - path if path.is_ident("u64") => { - repr.repr = Repr::U64; - } - path if path.is_ident("i64") => { - repr.repr = Repr::I64; - } - path if path.is_ident("u128") => { - repr.repr = Repr::U128; - } - path if path.is_ident("i128") => { - repr.repr = Repr::I128; - } - path if path.is_ident("packed") => { - let packed_alignment = match meta { - Meta::Path(_) => 1, - Meta::List(list) => { - if list.nested.len() != 1 { - // `repr(packed(n))` must have exactly one nested item. - continue; - } + impl Repr { + fn is_integer(self) -> bool { + match self { + Repr::Rust | Repr::C | Repr::Transparent => false, + _ => true, + } + } - let nested = &list.nested[0]; - let int_lit = if let NestedMeta::Lit(Lit::Int(int_lit)) = nested { - int_lit - } else { - // The nested item must be an integer literal. - continue; - }; - - let value = if let Ok(value) = int_lit.base10_parse::() { - value - } else { - // The literal must be positive and less than 2^29. - continue; - }; - value - } - Meta::NameValue(_) => { - // `repr(packed)` doesn't support name value syntax. - continue; - } - }; - - let new_packed_alignment = match repr.packed { - Some(prev) => u32::min(prev, packed_alignment), - None => packed_alignment, - }; - repr.packed = Some(new_packed_alignment); - } - _ => {} + fn as_integer_type(self) -> Option { + match self { + Repr::Rust | Repr::C | Repr::Transparent => None, + $( + Repr::$Xn => Some(quote! { ::core::primitive::$xn }), + )* } } } - repr -} + #[derive(Clone, Copy)] + struct Representation { + packed: Option, + repr: Repr, + } + + impl Default for Representation { + fn default() -> Self { + Self { packed: None, repr: Repr::Rust } + } + } + + impl Parse for Representation { + fn parse(input: ParseStream<'_>) -> Result { + let mut ret = Representation::default(); + while !input.is_empty() { + let keyword = input.parse::()?; + // preƫmptively call `.to_string()` *once* (rather than on `is_ident()`) + let keyword_str = keyword.to_string(); + let new_repr = match keyword_str.as_str() { + "C" => Repr::C, + "transparent" => Repr::Transparent, + "packed" => { + ret.packed = Some(if input.peek(token::Paren) { + let contents; parenthesized!(contents in input); + LitInt::base10_parse::(&contents.parse()?)? + } else { + 1 + }); + let _: Option = input.parse()?; + continue; + }, + $( + stringify!($xn) => Repr::$Xn, + )* + _ => return Err(input.error("unrecognized representation hint")) + }; + if ::core::mem::replace(&mut ret.repr, new_repr) != Repr::Rust { + input.error("duplicate representation hint"); + } + let _: Option = input.parse()?; + } + Ok(ret) + } + } + + impl ToTokens for Representation { + fn to_tokens(&self, tokens: &mut TokenStream) { + let repr = match self.repr { + Repr::Rust => None, + Repr::C => Some(quote!(C)), + Repr::Transparent => Some(quote!(transparent)), + $( + Repr::$Xn => Some(quote!($xn)), + )* + }; + let packed = self.packed.map(|p| quote!(packed(#p))); + let comma = if packed.is_some() && repr.is_some() { + Some(quote!(,)) + } else { + None + }; + tokens.extend(quote!( + #[repr( #repr #comma #packed )] + )); + } + } +)} use mk_repr; struct VariantDiscriminantIterator<'a, I: Iterator + 'a> { inner: I, @@ -764,12 +725,15 @@ impl<'a, I: Iterator + 'a> impl<'a, I: Iterator + 'a> Iterator for VariantDiscriminantIterator<'a, I> { - type Item = Result; + type Item = Result; fn next(&mut self) -> Option { let variant = self.inner.next()?; if !variant.fields.is_empty() { - return Some(Err("Only fieldless enums are supported")); + return Some(Err(Error::new_spanned( + &variant.fields, + "Only fieldless enums are supported", + ))); } if let Some((_, discriminant)) = &variant.discriminant { @@ -786,14 +750,14 @@ impl<'a, I: Iterator + 'a> Iterator } } -fn parse_int_expr(expr: &Expr) -> Result { +fn parse_int_expr(expr: &Expr) -> Result { match expr { Expr::Unary(ExprUnary { op: UnOp::Neg(_), expr, .. }) => { parse_int_expr(expr).map(|int| -int) } Expr::Lit(ExprLit { lit: Lit::Int(int), .. }) => { - int.base10_parse().map_err(|_| "Invalid integer expression") + int.base10_parse() } - _ => Err("Not an integer expression"), + _ => bail!("Not an integer expression"), } }