diff --git a/bytecheck_derive/src/attributes.rs b/bytecheck_derive/src/attributes.rs index 1559c60..8de1bc3 100644 --- a/bytecheck_derive/src/attributes.rs +++ b/bytecheck_derive/src/attributes.rs @@ -5,8 +5,6 @@ use syn::{ WherePredicate, }; -use crate::repr::Repr; - fn try_set_attribute( attribute: &mut Option, value: T, @@ -25,7 +23,6 @@ fn try_set_attribute( #[derive(Default)] pub struct Attributes { - pub repr: Repr, pub bounds: Option>, crate_path: Option, pub verify: Option, @@ -78,10 +75,6 @@ impl Attributes { attr.parse_nested_meta(|nested| { result.parse_check_bytes_attributes(nested) })?; - } else if attr.path().is_ident("repr") { - attr.parse_nested_meta(|nested| { - result.repr.parse_list_meta(nested) - })?; } } diff --git a/bytecheck_derive/src/lib.rs b/bytecheck_derive/src/lib.rs index a6e65ec..19c6148 100644 --- a/bytecheck_derive/src/lib.rs +++ b/bytecheck_derive/src/lib.rs @@ -22,7 +22,7 @@ use syn::{ use crate::{ attributes::{Attributes, FieldAttributes}, - repr::BaseRepr, + repr::Repr, util::iter_fields, }; @@ -273,27 +273,28 @@ fn derive_check_bytes(mut input: DeriveInput) -> Result { } }, Data::Enum(ref data) => { - let repr = match attributes.repr.base_repr { - None => { + let repr = Repr::from_attrs(&input.attrs)?; + let primitive = match repr { + Repr::Transparent => { return Err(Error::new_spanned( name, - "enums implementing CheckBytes must have an explicit \ - repr", + "enums cannot be repr(transparent)", )) } - Some((BaseRepr::Transparent, _)) => { + Repr::Primitive(i) => i, + Repr::C { .. } => { return Err(Error::new_spanned( name, - "enums cannot be repr(transparent)", + "repr(C) enums are not currently supported", )) } - Some((BaseRepr::C, _)) => { + Repr::Rust { .. } => { return Err(Error::new_spanned( name, - "repr(C) enums are not currently supported", + "enums implementing CheckBytes must have an explicit \ + repr", )) } - Some((BaseRepr::Int(i), _)) => i, }; let tag_variant_defs = data.variants.iter().map(|v| { @@ -309,7 +310,7 @@ fn derive_check_bytes(mut input: DeriveInput) -> Result { let variant = &v.ident; quote! { #[allow(non_upper_case_globals)] - const #variant: #repr = Tag::#variant as #repr; + const #variant: #primitive = Tag::#variant as #primitive; } }); @@ -415,7 +416,7 @@ fn derive_check_bytes(mut input: DeriveInput) -> Result { quote! { const _: () = { - #[repr(#repr)] + #[repr(#primitive)] enum Tag { #(#tag_variant_defs,)* } @@ -448,7 +449,7 @@ fn derive_check_bytes(mut input: DeriveInput) -> Result { (), <__C as #crate_path::rancor::Fallible>::Error, > { - let tag = *value.cast::<#repr>(); + let tag = *value.cast::<#primitive>(); match tag { #(#tag_variant_values => #check_arms)* _ => #no_matching_tag_arm, diff --git a/bytecheck_derive/src/repr.rs b/bytecheck_derive/src/repr.rs index af41d70..d1068e1 100644 --- a/bytecheck_derive/src/repr.rs +++ b/bytecheck_derive/src/repr.rs @@ -1,176 +1,137 @@ use proc_macro2::{Span, TokenStream}; -use quote::{quote, ToTokens, TokenStreamExt}; -use syn::{ - meta::ParseNestedMeta, parenthesized, spanned::Spanned, Error, LitInt, -}; +use quote::{quote, ToTokens}; +use syn::{parenthesized, token, Attribute, Error, Ident, LitInt}; #[derive(Clone, Copy)] -pub enum IntRepr { +pub enum Primitive { I8, I16, I32, I64, - I128, + Isize, U8, U16, U32, U64, - U128, + Usize, } -impl ToTokens for IntRepr { - fn to_tokens(&self, tokens: &mut TokenStream) { +impl Primitive { + const ALL: [Self; 10] = [ + Self::I8, + Self::I16, + Self::I32, + Self::I64, + Self::Isize, + Self::U8, + Self::U16, + Self::U32, + Self::U64, + Self::Usize, + ]; + + pub const fn as_str(&self) -> &'static str { match self { - Self::I8 => tokens.append_all(quote! { i8 }), - Self::I16 => tokens.append_all(quote! { i16 }), - Self::I32 => tokens.append_all(quote! { i32 }), - Self::I64 => tokens.append_all(quote! { i64 }), - Self::I128 => tokens.append_all(quote! { i128 }), - Self::U8 => tokens.append_all(quote! { u8 }), - Self::U16 => tokens.append_all(quote! { u16 }), - Self::U32 => tokens.append_all(quote! { u32 }), - Self::U64 => tokens.append_all(quote! { u64 }), - Self::U128 => tokens.append_all(quote! { u128 }), + Self::I8 => "i8", + Self::I16 => "i16", + Self::I32 => "i32", + Self::I64 => "i64", + Self::Isize => "isize", + Self::U8 => "u8", + Self::U16 => "u16", + Self::U32 => "u32", + Self::U64 => "u64", + Self::Usize => "usize", } } } -#[derive(Clone, Copy)] -pub enum BaseRepr { - C, - // structs only - Transparent, - // enums only - Int(IntRepr), -} - -impl ToTokens for BaseRepr { +impl ToTokens for Primitive { fn to_tokens(&self, tokens: &mut TokenStream) { - match self { - BaseRepr::C => tokens.append_all(quote! { C }), - BaseRepr::Transparent => tokens.append_all(quote! { transparent }), - BaseRepr::Int(int_repr) => tokens.append_all(quote! { #int_repr }), - } + let ident = Ident::new(self.as_str(), Span::call_site()); + tokens.extend(quote! { #ident }); } } -#[derive(Clone)] pub enum Modifier { - // structs only - Packed, - Align(LitInt), + Packed(#[allow(dead_code)] usize), + Align(#[allow(dead_code)] usize), } -impl ToTokens for Modifier { - fn to_tokens(&self, tokens: &mut TokenStream) { - match self { - Modifier::Packed => tokens.append_all(quote! { packed }), - Modifier::Align(n) => tokens.append_all(quote! { align(#n) }), - } - } -} - -#[derive(Clone, Default)] -pub struct Repr { - pub base_repr: Option<(BaseRepr, Span)>, - pub modifier: Option<(Modifier, Span)>, +pub enum Repr { + Transparent, + Primitive(Primitive), + C { + #[allow(dead_code)] + primitive: Option, + #[allow(dead_code)] + modifier: Option, + }, + Rust { + #[allow(dead_code)] + modifier: Option, + }, } impl Repr { - fn try_set_modifier( - &mut self, - modifier: Modifier, - spanned: S, - ) -> Result<(), Error> { - if self.modifier.is_some() { - Err(Error::new_spanned( - spanned, - "only one repr modifier may be specified", - )) - } else { - self.modifier = Some((modifier, spanned.span())); - Ok(()) + pub fn from_attrs(attrs: &[Attribute]) -> Result { + let mut c = false; + let mut transparent = false; + let mut primitive = None; + let mut modifier = None; + + for attr in attrs.iter().filter(|a| a.meta.path().is_ident("repr")) { + attr.parse_nested_meta(|meta| { + if meta.path.is_ident("C") { + c = true; + Ok(()) + } else if meta.path.is_ident("transparent") { + transparent = true; + Ok(()) + } else if let Some(&p) = Primitive::ALL + .iter() + .find(|p| meta.path.is_ident(p.as_str())) + { + primitive = Some(p); + Ok(()) + } else if meta.path.is_ident("align") { + let content; + parenthesized!(content in meta.input); + let lit = content.parse::()?; + let n = lit.base10_parse()?; + modifier = Some(Modifier::Align(n)); + Ok(()) + } else if meta.path.is_ident("packed") { + if meta.input.peek(token::Paren) { + let content; + parenthesized!(content in meta.input); + let lit = content.parse::()?; + let n = lit.base10_parse()?; + modifier = Some(Modifier::Packed(n)); + } else { + modifier = Some(Modifier::Packed(1)); + } + Ok(()) + } else { + Err(Error::new_spanned( + meta.path, + "unrecognized repr argument", + )) + } + })?; } - } - fn try_set_base_repr( - &mut self, - repr: BaseRepr, - spanned: S, - ) -> Result<(), Error> { - if self.base_repr.is_some() { - Err(Error::new_spanned( - spanned, - "only one repr may be specified", - )) + if c { + Ok(Repr::C { + primitive, + modifier, + }) + } else if transparent { + Ok(Repr::Transparent) + } else if let Some(primitive) = primitive { + Ok(Repr::Primitive(primitive)) } else { - self.base_repr = Some((repr, spanned.span())); - Ok(()) + Ok(Repr::Rust { modifier }) } } - - pub fn parse_list_meta( - &mut self, - meta: ParseNestedMeta<'_>, - ) -> Result<(), Error> { - if meta.path.is_ident("packed") { - return self.try_set_modifier(Modifier::Packed, meta.path); - } else if meta.path.is_ident("align") { - let content; - parenthesized!(content in meta.input); - let alignment = content.parse()?; - - if !content.is_empty() { - return Err(content.error("align requires only one argument")); - } - - return self - .try_set_modifier(Modifier::Align(alignment), meta.path); - } - - let parsed_repr = if meta.path.is_ident("transparent") { - BaseRepr::Transparent - } else if meta.path.is_ident("C") { - BaseRepr::C - } else if meta.path.is_ident("i8") { - BaseRepr::Int(IntRepr::I8) - } else if meta.path.is_ident("i16") { - BaseRepr::Int(IntRepr::I16) - } else if meta.path.is_ident("i32") { - BaseRepr::Int(IntRepr::I32) - } else if meta.path.is_ident("i64") { - BaseRepr::Int(IntRepr::I64) - } else if meta.path.is_ident("i128") { - BaseRepr::Int(IntRepr::I128) - } else if meta.path.is_ident("u8") { - BaseRepr::Int(IntRepr::U8) - } else if meta.path.is_ident("u16") { - BaseRepr::Int(IntRepr::U16) - } else if meta.path.is_ident("u32") { - BaseRepr::Int(IntRepr::U32) - } else if meta.path.is_ident("u64") { - BaseRepr::Int(IntRepr::U64) - } else if meta.path.is_ident("u128") { - BaseRepr::Int(IntRepr::U128) - } else { - let msg = - "invalid repr, available reprs are transparent, C, i* and u*"; - - return Err(Error::new_spanned(meta.path, msg)); - }; - - self.try_set_base_repr(parsed_repr, meta.path) - } -} - -impl ToTokens for Repr { - fn to_tokens(&self, tokens: &mut TokenStream) { - let base_repr = self.base_repr.as_ref().map(|(b, _)| b); - let base_repr_iter = base_repr.iter(); - let modifier = self.modifier.as_ref().map(|(m, _)| m); - let modifier_iter = modifier.iter(); - tokens.append_all( - quote! { #[repr(#(#base_repr_iter,)* #(#modifier_iter,)*)] }, - ); - } }