From 558843eb41c3747c26b45aac978e6bb53fe510e7 Mon Sep 17 00:00:00 2001 From: Joshua Liebow-Feeser Date: Sun, 15 Sep 2024 16:57:49 -0700 Subject: [PATCH] [derive] Test exact output of derives Makes progress on #367, #1634 --- zerocopy-derive/Cargo.toml | 1 + zerocopy-derive/src/lib.rs | 4 +- zerocopy-derive/src/output_tests.rs | 193 ++++++++++++++++++++++++++++ 3 files changed, 197 insertions(+), 1 deletion(-) create mode 100644 zerocopy-derive/src/output_tests.rs diff --git a/zerocopy-derive/Cargo.toml b/zerocopy-derive/Cargo.toml index 745c9d9cab..b5799c170d 100644 --- a/zerocopy-derive/Cargo.toml +++ b/zerocopy-derive/Cargo.toml @@ -37,6 +37,7 @@ syn = "2.0.46" once_cell = "=1.9" rustversion = "1.0" static_assertions = "1.1" +synstructure = "0.13.1" testutil = { path = "../testutil" } # Pinned to a specific version so that the version used for local development # and the version used in CI are guaranteed to be the same. Future versions diff --git a/zerocopy-derive/src/lib.rs b/zerocopy-derive/src/lib.rs index d144e04629..c52c9cb829 100644 --- a/zerocopy-derive/src/lib.rs +++ b/zerocopy-derive/src/lib.rs @@ -30,6 +30,8 @@ mod r#enum; mod ext; +#[cfg(test)] +mod output_tests; mod repr; use proc_macro2::{TokenStream, TokenTree}; @@ -419,7 +421,7 @@ fn derive_try_from_bytes_struct(ast: &DeriveInput, strct: &DataStruct) -> proc_m // validity of a struct is just the composition of the bit // validities of its fields, so this is a sound implementation of // `is_bit_valid`. - fn is_bit_valid>( + fn is_bit_valid >( mut candidate: ::zerocopy::Maybe ) -> bool { true #(&& { diff --git a/zerocopy-derive/src/output_tests.rs b/zerocopy-derive/src/output_tests.rs new file mode 100644 index 0000000000..8ca9bd2728 --- /dev/null +++ b/zerocopy-derive/src/output_tests.rs @@ -0,0 +1,193 @@ +// Copyright 2024 The Fuchsia Authors +// +// Licensed under a BSD-style license , Apache License, Version 2.0 +// , or the MIT +// license , at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +use synstructure::test_derive; + +macro_rules! impl_synstructure_derive { + ($($outer:ident => $inner:ident),* $(,)?) => { + $( + #[allow(non_snake_case)] + fn $outer(s: synstructure::Structure) -> proc_macro2::TokenStream { + super::$inner(s.ast()) + } + )* + }; +} + +impl_synstructure_derive!( + KnownLayout => derive_known_layout_inner, + Immutable => derive_no_cell_inner, + TryFromBytes => derive_try_from_bytes_inner, + FromZeros => derive_from_zeros_inner, + FromBytes => derive_from_bytes_inner, + IntoBytes => derive_into_bytes_inner, + Unaligned => derive_unaligned_inner, +); + +#[test] +fn test_known_layout() { + test_derive! { + KnownLayout { + struct Foo; + } expands to { + #[allow(deprecated)] + unsafe impl<> ::zerocopy::KnownLayout for Foo<> + where + Self: ::zerocopy::util::macro_util::core_reexport::marker::Sized, + { + fn only_derive_is_allowed_to_implement_this_trait() {} + + type PointerMetadata = (); + + const LAYOUT: ::zerocopy::DstLayout = ::zerocopy::DstLayout::for_type::(); + + #[inline(always)] + fn raw_from_ptr_len( + bytes: ::zerocopy::util::macro_util::core_reexport::ptr::NonNull, + _meta: (), + ) -> ::zerocopy::util::macro_util::core_reexport::ptr::NonNull { + bytes.cast::() + } + + #[inline(always)] + fn pointer_to_metadata( + _ptr: ::zerocopy::util::macro_util::core_reexport::ptr::NonNull, + ) -> () { + } + } + } no_build + } +} + +#[test] +fn test_immutable() { + test_derive! { + Immutable { + struct Foo; + } expands to { + #[allow(deprecated)] + unsafe impl<> ::zerocopy::Immutable for Foo<> where { + fn only_derive_is_allowed_to_implement_this_trait() {} + } + } no_build + } +} + +#[test] +fn test_try_from_bytes() { + test_derive! { + TryFromBytes { + struct Foo; + } expands to { + #[allow(deprecated)] + unsafe impl<> ::zerocopy::TryFromBytes for Foo<> where { + fn only_derive_is_allowed_to_implement_this_trait() {} + + fn is_bit_valid< + A: ::zerocopy::pointer::invariant::Aliasing + + ::zerocopy::pointer::invariant::AtLeast<::zerocopy::pointer::invariant::Shared> + >( + mut candidate: ::zerocopy::Maybe + ) -> bool { + true + } + } + } no_build + } +} + +#[test] +fn test_from_zeros() { + test_derive! { + FromZeros { + struct Foo; + } expands to { + #[allow(deprecated)] + unsafe impl<> ::zerocopy::TryFromBytes for Foo<> where { + fn only_derive_is_allowed_to_implement_this_trait() {} + + fn is_bit_valid< + A: ::zerocopy::pointer::invariant::Aliasing + + ::zerocopy::pointer::invariant::AtLeast<::zerocopy::pointer::invariant::Shared> + >( + mut candidate: ::zerocopy::Maybe + ) -> bool { + true + } + } + + #[allow(deprecated)] + unsafe impl<> ::zerocopy::FromZeros for Foo<> where { + fn only_derive_is_allowed_to_implement_this_trait() {} + } + } no_build + } +} + +#[test] +fn test_from_bytes() { + test_derive! { + FromBytes { + struct Foo; + } expands to { + #[allow(deprecated)] + unsafe impl<> ::zerocopy::TryFromBytes for Foo<> where { + fn only_derive_is_allowed_to_implement_this_trait() {} + + fn is_bit_valid< + A: ::zerocopy::pointer::invariant::Aliasing + + ::zerocopy::pointer::invariant::AtLeast<::zerocopy::pointer::invariant::Shared> + >( + mut candidate: ::zerocopy::Maybe + ) -> bool { + true + } + } + + #[allow(deprecated)] + unsafe impl<> ::zerocopy::FromZeros for Foo<> where { + fn only_derive_is_allowed_to_implement_this_trait() {} + } + + #[allow(deprecated)] + unsafe impl<> ::zerocopy::FromBytes for Foo<> where { + fn only_derive_is_allowed_to_implement_this_trait() {} + } + } no_build + } +} + +#[test] +fn test_into_bytes() { + test_derive! { + IntoBytes { + #[repr(C)] + struct Foo; + } expands to { + #[allow(deprecated)] + unsafe impl<> ::zerocopy::IntoBytes for Foo<> where { + fn only_derive_is_allowed_to_implement_this_trait() {} + } + } no_build + } +} + +#[test] +fn test_unaligned() { + test_derive! { + Unaligned { + #[repr(C)] + struct Foo; + } expands to { + #[allow(deprecated)] + unsafe impl<> ::zerocopy::Unaligned for Foo<> where { + fn only_derive_is_allowed_to_implement_this_trait() {} + } + } no_build + } +}