Added custom crate path with tests (#209)

Co-authored-by: Andri <80914617+Shuray04@users.noreply.github.com>
This commit is contained in:
Andri 2023-10-20 07:02:54 +02:00 committed by GitHub
parent fd27a5b018
commit c705218630
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 123 additions and 67 deletions

View File

@ -9,8 +9,8 @@ use quote::quote;
use syn::{parse_macro_input, DeriveInput, Result};
use crate::traits::{
AnyBitPattern, CheckedBitPattern, Contiguous, Derivable, NoUninit, Pod,
TransparentWrapper, Zeroable,
bytemuck_crate_name, AnyBitPattern, CheckedBitPattern, Contiguous, Derivable,
NoUninit, Pod, TransparentWrapper, Zeroable,
};
/// Derive the `Pod` trait for a struct
@ -87,7 +87,7 @@ use crate::traits::{
///
/// let _: u32 = bytemuck::cast(Generic { a: 4u32, b: PhantomData::<NotPod> });
/// ```
#[proc_macro_derive(Pod)]
#[proc_macro_derive(Pod, attributes(bytemuck))]
pub fn derive_pod(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let expanded =
derive_marker_trait::<Pod>(parse_macro_input!(input as DeriveInput));
@ -103,7 +103,7 @@ pub fn derive_pod(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
/// The following constraints need to be satisfied for the macro to succeed
///
/// - All fields in the struct must to implement `AnyBitPattern`
#[proc_macro_derive(AnyBitPattern)]
#[proc_macro_derive(AnyBitPattern, attributes(bytemuck))]
pub fn derive_anybitpattern(
input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
@ -192,7 +192,7 @@ pub fn derive_anybitpattern(
/// # }
/// ZeroableWhenTIsZeroable::<String>::zeroed();
/// ```
#[proc_macro_derive(Zeroable, attributes(zeroable))]
#[proc_macro_derive(Zeroable, attributes(bytemuck, zeroable))]
pub fn derive_zeroable(
input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
@ -319,7 +319,7 @@ pub fn derive_maybe_pod(
/// another_extra: NonTransparentSafeZST, // not `Zeroable`
/// }
/// ```
#[proc_macro_derive(TransparentWrapper, attributes(transparent))]
#[proc_macro_derive(TransparentWrapper, attributes(bytemuck, transparent))]
pub fn derive_transparent(
input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
@ -394,6 +394,7 @@ pub fn derive_byte_eq(
input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let crate_name = bytemuck_crate_name(&input);
let ident = input.ident;
proc_macro::TokenStream::from(quote! {
@ -401,7 +402,7 @@ pub fn derive_byte_eq(
#[inline]
#[must_use]
fn eq(&self, other: &Self) -> bool {
::bytemuck::bytes_of(self) == ::bytemuck::bytes_of(other)
#crate_name::bytes_of(self) == #crate_name::bytes_of(other)
}
}
impl ::core::cmp::Eq for #ident { }
@ -434,18 +435,19 @@ pub fn derive_byte_hash(
input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let crate_name = bytemuck_crate_name(&input);
let ident = input.ident;
proc_macro::TokenStream::from(quote! {
impl ::core::hash::Hash for #ident {
#[inline]
fn hash<H: ::core::hash::Hasher>(&self, state: &mut H) {
::core::hash::Hash::hash_slice(::bytemuck::bytes_of(self), state)
::core::hash::Hash::hash_slice(#crate_name::bytes_of(self), state)
}
#[inline]
fn hash_slice<H: ::core::hash::Hasher>(data: &[Self], state: &mut H) {
::core::hash::Hash::hash_slice(::bytemuck::cast_slice::<_, u8>(data), state)
::core::hash::Hash::hash_slice(#crate_name::cast_slice::<_, u8>(data), state)
}
}
})
@ -518,7 +520,8 @@ fn find_and_parse_helper_attributes<P: syn::parse::Parser + Copy>(
fn derive_marker_trait_inner<Trait: Derivable>(
mut input: DeriveInput,
) -> Result<TokenStream> {
let trait_ = Trait::ident(&input)?;
let crate_name = bytemuck_crate_name(&input);
let trait_ = Trait::ident(&input, &crate_name)?;
// If this trait allows explicit bounds, and any explicit bounds were given,
// then use those explicit bounds. Else, apply the default bounds (bound
// each generic type on this trait).
@ -585,10 +588,12 @@ fn derive_marker_trait_inner<Trait: Derivable>(
input.generics.split_for_impl();
Trait::check_attributes(&input.data, &input.attrs)?;
let asserts = Trait::asserts(&input)?;
let (trait_impl_extras, trait_impl) = Trait::trait_impl(&input)?;
let asserts = Trait::asserts(&input, &crate_name)?;
let (trait_impl_extras, trait_impl) = Trait::trait_impl(&input, &crate_name)?;
let implies_trait = if let Some(implies_trait) = Trait::implies_trait() {
let implies_trait = if let Some(implies_trait) =
Trait::implies_trait(&crate_name)
{
quote!(unsafe impl #impl_generics #implies_trait for #name #ty_generics #where_clause {})
} else {
quote!()

View File

@ -21,17 +21,17 @@ macro_rules! bail {
}
pub trait Derivable {
fn ident(input: &DeriveInput) -> Result<syn::Path>;
fn implies_trait() -> Option<TokenStream> {
fn ident(input: &DeriveInput, crate_name: &TokenStream) -> Result<syn::Path>;
fn implies_trait(_crate_name: &TokenStream) -> Option<TokenStream> {
None
}
fn asserts(_input: &DeriveInput) -> Result<TokenStream> {
fn asserts(_input: &DeriveInput, _crate_name: &TokenStream) -> Result<TokenStream> {
Ok(quote!())
}
fn check_attributes(_ty: &Data, _attributes: &[Attribute]) -> Result<()> {
Ok(())
}
fn trait_impl(_input: &DeriveInput) -> Result<(TokenStream, TokenStream)> {
fn trait_impl(_input: &DeriveInput, _crate_name: &TokenStream) -> Result<(TokenStream, TokenStream)> {
Ok((quote!(), quote!()))
}
fn requires_where_clause() -> bool {
@ -45,11 +45,11 @@ pub trait Derivable {
pub struct Pod;
impl Derivable for Pod {
fn ident(_: &DeriveInput) -> Result<syn::Path> {
Ok(syn::parse_quote!(::bytemuck::Pod))
fn ident(_: &DeriveInput, crate_name: &TokenStream) -> Result<syn::Path> {
Ok(syn::parse_quote!(#crate_name::Pod))
}
fn asserts(input: &DeriveInput) -> Result<TokenStream> {
fn asserts(input: &DeriveInput, crate_name: &TokenStream) -> Result<TokenStream> {
let repr = get_repr(&input.attrs)?;
let completly_packed =
@ -71,7 +71,7 @@ impl Derivable for Pod {
None
};
let assert_fields_are_pod =
generate_fields_are_trait(input, Self::ident(input)?)?;
generate_fields_are_trait(input, Self::ident(input, crate_name)?)?;
Ok(quote!(
#assert_no_padding
@ -98,18 +98,18 @@ impl Derivable for Pod {
pub struct AnyBitPattern;
impl Derivable for AnyBitPattern {
fn ident(_: &DeriveInput) -> Result<syn::Path> {
Ok(syn::parse_quote!(::bytemuck::AnyBitPattern))
fn ident(_: &DeriveInput, crate_name: &TokenStream) -> Result<syn::Path> {
Ok(syn::parse_quote!(#crate_name::AnyBitPattern))
}
fn implies_trait() -> Option<TokenStream> {
Some(quote!(::bytemuck::Zeroable))
fn implies_trait(crate_name: &TokenStream) -> Option<TokenStream> {
Some(quote!(#crate_name::Zeroable))
}
fn asserts(input: &DeriveInput) -> Result<TokenStream> {
fn asserts(input: &DeriveInput, crate_name: &TokenStream) -> Result<TokenStream> {
match &input.data {
Data::Union(_) => Ok(quote!()), // unions are always `AnyBitPattern`
Data::Struct(_) => generate_fields_are_trait(input, Self::ident(input)?),
Data::Struct(_) => generate_fields_are_trait(input, Self::ident(input, crate_name)?),
Data::Enum(_) => {
bail!("Deriving AnyBitPattern is not supported for enums")
}
@ -120,14 +120,14 @@ impl Derivable for AnyBitPattern {
pub struct Zeroable;
impl Derivable for Zeroable {
fn ident(_: &DeriveInput) -> Result<syn::Path> {
Ok(syn::parse_quote!(::bytemuck::Zeroable))
fn ident(_: &DeriveInput, crate_name: &TokenStream) -> Result<syn::Path> {
Ok(syn::parse_quote!(#crate_name::Zeroable))
}
fn asserts(input: &DeriveInput) -> Result<TokenStream> {
fn asserts(input: &DeriveInput, crate_name: &TokenStream) -> Result<TokenStream> {
match &input.data {
Data::Union(_) => Ok(quote!()), // unions are always `Zeroable`
Data::Struct(_) => generate_fields_are_trait(input, Self::ident(input)?),
Data::Struct(_) => generate_fields_are_trait(input, Self::ident(input, crate_name)?),
Data::Enum(_) => bail!("Deriving Zeroable is not supported for enums"),
}
}
@ -140,8 +140,8 @@ impl Derivable for Zeroable {
pub struct NoUninit;
impl Derivable for NoUninit {
fn ident(_: &DeriveInput) -> Result<syn::Path> {
Ok(syn::parse_quote!(::bytemuck::NoUninit))
fn ident(_: &DeriveInput, crate_name: &TokenStream) -> Result<syn::Path> {
Ok(syn::parse_quote!(#crate_name::NoUninit))
}
fn check_attributes(ty: &Data, attributes: &[Attribute]) -> Result<()> {
@ -160,7 +160,7 @@ impl Derivable for NoUninit {
}
}
fn asserts(input: &DeriveInput) -> Result<TokenStream> {
fn asserts(input: &DeriveInput, crate_name: &TokenStream) -> Result<TokenStream> {
if !input.generics.params.is_empty() {
bail!("NoUninit cannot be derived for structs containing generic parameters because the padding requirements can't be verified for generic structs");
}
@ -169,7 +169,7 @@ impl Derivable for NoUninit {
Data::Struct(DataStruct { .. }) => {
let assert_no_padding = generate_assert_no_padding(&input)?;
let assert_fields_are_no_padding =
generate_fields_are_trait(&input, Self::ident(input)?)?;
generate_fields_are_trait(&input, Self::ident(input, crate_name)?)?;
Ok(quote!(
#assert_no_padding
@ -187,7 +187,7 @@ impl Derivable for NoUninit {
}
}
fn trait_impl(_input: &DeriveInput) -> Result<(TokenStream, TokenStream)> {
fn trait_impl(_input: &DeriveInput, _crate_name: &TokenStream) -> Result<(TokenStream, TokenStream)> {
Ok((quote!(), quote!()))
}
}
@ -195,8 +195,8 @@ impl Derivable for NoUninit {
pub struct CheckedBitPattern;
impl Derivable for CheckedBitPattern {
fn ident(_: &DeriveInput) -> Result<syn::Path> {
Ok(syn::parse_quote!(::bytemuck::CheckedBitPattern))
fn ident(_: &DeriveInput, crate_name: &TokenStream) -> Result<syn::Path> {
Ok(syn::parse_quote!(#crate_name::CheckedBitPattern))
}
fn check_attributes(ty: &Data, attributes: &[Attribute]) -> Result<()> {
@ -223,7 +223,7 @@ impl Derivable for CheckedBitPattern {
}
}
fn asserts(input: &DeriveInput) -> Result<TokenStream> {
fn asserts(input: &DeriveInput, crate_name: &TokenStream) -> Result<TokenStream> {
if !input.generics.params.is_empty() {
bail!("CheckedBitPattern cannot be derived for structs containing generic parameters");
}
@ -231,7 +231,7 @@ impl Derivable for CheckedBitPattern {
match &input.data {
Data::Struct(DataStruct { .. }) => {
let assert_fields_are_maybe_pod =
generate_fields_are_trait(&input, Self::ident(input)?)?;
generate_fields_are_trait(&input, Self::ident(input, crate_name)?)?;
Ok(assert_fields_are_maybe_pod)
}
@ -240,13 +240,13 @@ impl Derivable for CheckedBitPattern {
}
}
fn trait_impl(input: &DeriveInput) -> Result<(TokenStream, TokenStream)> {
fn trait_impl(input: &DeriveInput, crate_name: &TokenStream) -> Result<(TokenStream, TokenStream)> {
match &input.data {
Data::Struct(DataStruct { fields, .. }) => {
generate_checked_bit_pattern_struct(&input.ident, fields, &input.attrs)
generate_checked_bit_pattern_struct(&input.ident, fields, &input.attrs, crate_name)
}
Data::Enum(DataEnum { variants, .. }) => {
generate_checked_bit_pattern_enum(input, variants)
generate_checked_bit_pattern_enum(input, variants, crate_name)
}
Data::Union(_) => bail!("Internal error in CheckedBitPattern derive"), /* shouldn't be possible since we already error in attribute check for this case */
}
@ -274,7 +274,7 @@ impl TransparentWrapper {
}
impl Derivable for TransparentWrapper {
fn ident(input: &DeriveInput) -> Result<syn::Path> {
fn ident(input: &DeriveInput, crate_name: &TokenStream) -> Result<syn::Path> {
let fields = get_struct_fields(input)?;
let ty = match Self::get_wrapper_type(&input.attrs, &fields) {
@ -287,10 +287,10 @@ impl Derivable for TransparentWrapper {
),
};
Ok(syn::parse_quote!(::bytemuck::TransparentWrapper<#ty>))
Ok(syn::parse_quote!(#crate_name::TransparentWrapper<#ty>))
}
fn asserts(input: &DeriveInput) -> Result<TokenStream> {
fn asserts(input: &DeriveInput, crate_name: &TokenStream) -> Result<TokenStream> {
let (impl_generics, _ty_generics, where_clause) =
input.generics.split_for_impl();
let fields = get_struct_fields(input)?;
@ -318,7 +318,7 @@ impl Derivable for TransparentWrapper {
const _: () = {
#[repr(transparent)]
struct AssertWrappedIsWrapped #impl_generics((u8, ::core::marker::PhantomData<#wrapped_field_ty>), #(#nonwrapped_field_tys),*) #where_clause;
fn assert_zeroable<Z: ::bytemuck::Zeroable>() {}
fn assert_zeroable<Z: #crate_name::Zeroable>() {}
fn check #impl_generics () #where_clause {
#(
assert_zeroable::<#nonwrapped_field_tys>();
@ -352,11 +352,11 @@ impl Derivable for TransparentWrapper {
pub struct Contiguous;
impl Derivable for Contiguous {
fn ident(_: &DeriveInput) -> Result<syn::Path> {
Ok(syn::parse_quote!(::bytemuck::Contiguous))
fn ident(_: &DeriveInput, crate_name: &TokenStream) -> Result<syn::Path> {
Ok(syn::parse_quote!(#crate_name::Contiguous))
}
fn trait_impl(input: &DeriveInput) -> Result<(TokenStream, TokenStream)> {
fn trait_impl(input: &DeriveInput, _crate_name: &TokenStream) -> Result<(TokenStream, TokenStream)> {
let repr = get_repr(&input.attrs)?;
let integer_ty = if let Some(integer_ty) = repr.repr.as_integer() {
@ -460,7 +460,7 @@ fn get_field_types<'a>(
}
fn generate_checked_bit_pattern_struct(
input_ident: &Ident, fields: &Fields, attrs: &[Attribute],
input_ident: &Ident, fields: &Fields, attrs: &[Attribute], crate_name: &TokenStream
) -> Result<(TokenStream, TokenStream)> {
let bits_ty = Ident::new(&format!("{}Bits", input_ident), input_ident.span());
@ -486,10 +486,10 @@ fn generate_checked_bit_pattern_struct(
Ok((
quote! {
#repr
#[derive(Clone, Copy, ::bytemuck::AnyBitPattern)]
#[derive(Clone, Copy, #crate_name::AnyBitPattern)]
#derive_dbg
pub struct #bits_ty {
#(#field_name: <#field_ty as ::bytemuck::CheckedBitPattern>::Bits,)*
#(#field_name: <#field_ty as #crate_name::CheckedBitPattern>::Bits,)*
}
},
quote! {
@ -498,17 +498,17 @@ fn generate_checked_bit_pattern_struct(
#[inline]
#[allow(clippy::double_comparisons)]
fn is_valid_bit_pattern(bits: &#bits_ty) -> bool {
#(<#field_ty as ::bytemuck::CheckedBitPattern>::is_valid_bit_pattern(&{ bits.#field_name }) && )* true
#(<#field_ty as #crate_name::CheckedBitPattern>::is_valid_bit_pattern(&{ bits.#field_name }) && )* true
}
},
))
}
fn generate_checked_bit_pattern_enum(
input: &DeriveInput, variants: &Punctuated<Variant, Token![,]>,
input: &DeriveInput, variants: &Punctuated<Variant, Token![,]>, crate_name: &TokenStream
) -> Result<(TokenStream, TokenStream)> {
if enum_has_fields(variants.iter()) {
generate_checked_bit_pattern_enum_with_fields(input, variants)
generate_checked_bit_pattern_enum_with_fields(input, variants, crate_name)
} else {
generate_checked_bit_pattern_enum_without_fields(input, variants)
}
@ -574,7 +574,7 @@ fn generate_checked_bit_pattern_enum_without_fields(
}
fn generate_checked_bit_pattern_enum_with_fields(
input: &DeriveInput, variants: &Punctuated<Variant, Token![,]>,
input: &DeriveInput, variants: &Punctuated<Variant, Token![,]>, crate_name: &TokenStream
) -> Result<(TokenStream, TokenStream)> {
let representation = get_repr(&input.attrs)?;
let vis = &input.vis;
@ -617,7 +617,7 @@ fn generate_checked_bit_pattern_enum_with_fields(
let fields = v.fields.iter().map(|v| &v.ty);
quote! {
#[derive(::core::clone::Clone, ::core::marker::Copy, ::bytemuck::CheckedBitPattern)]
#[derive(::core::clone::Clone, ::core::marker::Copy, #crate_name::CheckedBitPattern)]
#[repr(C)]
#vis struct #variant_struct_ident(#(#fields),*);
}
@ -644,7 +644,7 @@ fn generate_checked_bit_pattern_enum_with_fields(
Ok(quote! {
#discriminant => {
let payload = unsafe { &bits.payload.#ident };
<#variant_struct_ident as ::bytemuck::CheckedBitPattern>::is_valid_bit_pattern(payload)
<#variant_struct_ident as #crate_name::CheckedBitPattern>::is_valid_bit_pattern(payload)
}
})
})
@ -652,7 +652,7 @@ fn generate_checked_bit_pattern_enum_with_fields(
Ok((
quote! {
#[derive(::core::clone::Clone, ::core::marker::Copy, ::bytemuck::AnyBitPattern)]
#[derive(::core::clone::Clone, ::core::marker::Copy, #crate_name::AnyBitPattern)]
#derive_dbg
#bits_repr
#vis struct #bits_ty_ident {
@ -660,7 +660,7 @@ fn generate_checked_bit_pattern_enum_with_fields(
payload: #variants_union_ident,
}
#[derive(::core::clone::Clone, ::core::marker::Copy, ::bytemuck::AnyBitPattern)]
#[derive(::core::clone::Clone, ::core::marker::Copy, #crate_name::AnyBitPattern)]
#[repr(C)]
#[allow(non_snake_case)]
#vis union #variants_union_ident {
@ -703,17 +703,17 @@ fn generate_checked_bit_pattern_enum_with_fields(
Ok((
quote! {
#[derive(::core::clone::Clone, ::core::marker::Copy, ::bytemuck::CheckedBitPattern)]
#[derive(::core::clone::Clone, ::core::marker::Copy, #crate_name::CheckedBitPattern)]
#[repr(C)]
#vis struct #bits_ty(#(#fields),*);
},
quote! {
type Bits = <#bits_ty as ::bytemuck::CheckedBitPattern>::Bits;
type Bits = <#bits_ty as #crate_name::CheckedBitPattern>::Bits;
#[inline]
#[allow(clippy::double_comparisons)]
fn is_valid_bit_pattern(bits: &Self::Bits) -> bool {
<#bits_ty as ::bytemuck::CheckedBitPattern>::is_valid_bit_pattern(bits)
<#bits_ty as #crate_name::CheckedBitPattern>::is_valid_bit_pattern(bits)
}
},
))
@ -740,7 +740,7 @@ fn generate_checked_bit_pattern_enum_with_fields(
// adding the discriminant repr integer as first field, as described above
quote! {
#[derive(::core::clone::Clone, ::core::marker::Copy, ::bytemuck::CheckedBitPattern)]
#[derive(::core::clone::Clone, ::core::marker::Copy, #crate_name::CheckedBitPattern)]
#[repr(C)]
#vis struct #variant_struct_ident(#integer, #(#fields),*);
}
@ -767,7 +767,7 @@ fn generate_checked_bit_pattern_enum_with_fields(
Ok(quote! {
#discriminant => {
let payload = unsafe { &bits.#ident };
<#variant_struct_ident as ::bytemuck::CheckedBitPattern>::is_valid_bit_pattern(payload)
<#variant_struct_ident as #crate_name::CheckedBitPattern>::is_valid_bit_pattern(payload)
}
})
})
@ -775,7 +775,7 @@ fn generate_checked_bit_pattern_enum_with_fields(
Ok((
quote! {
#[derive(::core::clone::Clone, ::core::marker::Copy, ::bytemuck::AnyBitPattern)]
#[derive(::core::clone::Clone, ::core::marker::Copy, #crate_name::AnyBitPattern)]
#bits_repr
#[allow(non_snake_case)]
#vis union #bits_ty_ident {
@ -1219,3 +1219,47 @@ mod tests {
);
}
}
pub fn bytemuck_crate_name(input: &DeriveInput) -> TokenStream {
const ATTR_NAME: &'static str = "crate";
let mut crate_name = quote!(::bytemuck);
for attr in &input.attrs {
if !attr.path().is_ident("bytemuck") {
continue;
}
attr.parse_nested_meta(|meta| {
if meta.path.is_ident(ATTR_NAME) {
let expr: syn::Expr = meta.value()?.parse()?;
let mut value = &expr;
while let syn::Expr::Group(e) = value {
value = &e.expr;
}
if let syn::Expr::Lit(syn::ExprLit {
lit: syn::Lit::Str(lit), ..
}) = value
{
let suffix = lit.suffix();
if !suffix.is_empty() {
bail!(format!("Unexpected suffix `{}` on string literal", suffix))
}
let path: syn::Path = match lit.parse() {
Ok(path) => path,
Err(_) => {
bail!(format!("Failed to parse path: {:?}", lit.value()))
}
};
crate_name = path.into_token_stream();
} else {
bail!(
"Expected bytemuck `crate` attribute to be a string: `crate = \"...\"`",
)
}
}
Ok(())
}).unwrap();
}
return crate_name;
}

View File

@ -443,3 +443,10 @@ fn checkedbitpattern_transparent_enum_with_fields() {
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
#[repr(C, align(16))]
struct Issue127 {}
use bytemuck as reexport_name;
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable, bytemuck::ByteEq)]
#[bytemuck(crate = "reexport_name")]
#[repr(C)]
struct Issue93 {}