// Copyright 2025, Linaro Limited // Author(s): Manos Pitsidianakis // SPDX-License-Identifier: GPL-2.0-or-later use quote::quote; use super::*; macro_rules! derive_compile_fail { ($derive_fn:ident, $input:expr, $error_msg:expr) => {{ let input: proc_macro2::TokenStream = $input; let error_msg: &str = $error_msg; let derive_fn: fn(input: syn::DeriveInput) -> Result = $derive_fn; let input: syn::DeriveInput = syn::parse2(input).unwrap(); let result = derive_fn(input); let err = result.unwrap_err().into_compile_error(); assert_eq!( err.to_string(), quote! { ::core::compile_error! { #error_msg } }.to_string() ); }}; } macro_rules! derive_compile { ($derive_fn:ident, $input:expr, $($expected:tt)*) => {{ let input: proc_macro2::TokenStream = $input; let expected: proc_macro2::TokenStream = $($expected)*; let derive_fn: fn(input: syn::DeriveInput) -> Result = $derive_fn; let input: syn::DeriveInput = syn::parse2(input).unwrap(); let result = derive_fn(input).unwrap(); assert_eq!(result.to_string(), expected.to_string()); }}; } #[test] fn test_derive_object() { derive_compile_fail!( derive_object_or_error, quote! { #[derive(Object)] struct Foo { _unused: [u8; 0], } }, "#[repr(C)] required for #[derive(Object)]" ); derive_compile!( derive_object_or_error, quote! { #[derive(Object)] #[repr(C)] struct Foo { _unused: [u8; 0], } }, quote! { ::qemu_api::assert_field_type!( Foo, _unused, ::qemu_api::qom::ParentField<::ParentType> ); ::qemu_api::module_init! { MODULE_INIT_QOM => unsafe { ::qemu_api::bindings::type_register_static(&::TYPE_INFO); } } } ); } #[test] fn test_derive_tryinto() { derive_compile_fail!( derive_tryinto_or_error, quote! { #[derive(TryInto)] struct Foo { _unused: [u8; 0], } }, "#[repr(u8/u16/u32/u64) required for #[derive(TryInto)]" ); derive_compile!( derive_tryinto_or_error, quote! { #[derive(TryInto)] #[repr(u8)] enum Foo { First = 0, Second, } }, quote! { impl Foo { #[allow(dead_code)] pub const fn into_bits(self) -> u8 { self as u8 } #[allow(dead_code)] pub const fn from_bits(value: u8) -> Self { match ({ const First: u8 = Foo::First as u8; const Second: u8 = Foo::Second as u8; match value { First => core::result::Result::Ok(Foo::First), Second => core::result::Result::Ok(Foo::Second), _ => core::result::Result::Err(value), } }) { Ok(x) => x, Err(_) => panic!("invalid value for Foo"), } } } impl core::convert::TryFrom for Foo { type Error = u8; #[allow(ambiguous_associated_items)] fn try_from(value: u8) -> Result { const First: u8 = Foo::First as u8; const Second: u8 = Foo::Second as u8; match value { First => core::result::Result::Ok(Foo::First), Second => core::result::Result::Ok(Foo::Second), _ => core::result::Result::Err(value), } } } } ); }