1*a721d9a9SManos Pitsidianakis // Copyright 2025, Linaro Limited 2*a721d9a9SManos Pitsidianakis // Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org> 3*a721d9a9SManos Pitsidianakis // SPDX-License-Identifier: GPL-2.0-or-later 4*a721d9a9SManos Pitsidianakis 5*a721d9a9SManos Pitsidianakis use quote::quote; 6*a721d9a9SManos Pitsidianakis 7*a721d9a9SManos Pitsidianakis use super::*; 8*a721d9a9SManos Pitsidianakis 9*a721d9a9SManos Pitsidianakis macro_rules! derive_compile_fail { 10*a721d9a9SManos Pitsidianakis ($derive_fn:ident, $input:expr, $error_msg:expr) => {{ 11*a721d9a9SManos Pitsidianakis let input: proc_macro2::TokenStream = $input; 12*a721d9a9SManos Pitsidianakis let error_msg: &str = $error_msg; 13*a721d9a9SManos Pitsidianakis let derive_fn: fn(input: syn::DeriveInput) -> Result<proc_macro2::TokenStream, syn::Error> = 14*a721d9a9SManos Pitsidianakis $derive_fn; 15*a721d9a9SManos Pitsidianakis 16*a721d9a9SManos Pitsidianakis let input: syn::DeriveInput = syn::parse2(input).unwrap(); 17*a721d9a9SManos Pitsidianakis let result = derive_fn(input); 18*a721d9a9SManos Pitsidianakis let err = result.unwrap_err().into_compile_error(); 19*a721d9a9SManos Pitsidianakis assert_eq!( 20*a721d9a9SManos Pitsidianakis err.to_string(), 21*a721d9a9SManos Pitsidianakis quote! { ::core::compile_error! { #error_msg } }.to_string() 22*a721d9a9SManos Pitsidianakis ); 23*a721d9a9SManos Pitsidianakis }}; 24*a721d9a9SManos Pitsidianakis } 25*a721d9a9SManos Pitsidianakis 26*a721d9a9SManos Pitsidianakis macro_rules! derive_compile { 27*a721d9a9SManos Pitsidianakis ($derive_fn:ident, $input:expr, $($expected:tt)*) => {{ 28*a721d9a9SManos Pitsidianakis let input: proc_macro2::TokenStream = $input; 29*a721d9a9SManos Pitsidianakis let expected: proc_macro2::TokenStream = $($expected)*; 30*a721d9a9SManos Pitsidianakis let derive_fn: fn(input: syn::DeriveInput) -> Result<proc_macro2::TokenStream, syn::Error> = 31*a721d9a9SManos Pitsidianakis $derive_fn; 32*a721d9a9SManos Pitsidianakis 33*a721d9a9SManos Pitsidianakis let input: syn::DeriveInput = syn::parse2(input).unwrap(); 34*a721d9a9SManos Pitsidianakis let result = derive_fn(input).unwrap(); 35*a721d9a9SManos Pitsidianakis assert_eq!(result.to_string(), expected.to_string()); 36*a721d9a9SManos Pitsidianakis }}; 37*a721d9a9SManos Pitsidianakis } 38*a721d9a9SManos Pitsidianakis 39*a721d9a9SManos Pitsidianakis #[test] 40*a721d9a9SManos Pitsidianakis fn test_derive_object() { 41*a721d9a9SManos Pitsidianakis derive_compile_fail!( 42*a721d9a9SManos Pitsidianakis derive_object_or_error, 43*a721d9a9SManos Pitsidianakis quote! { 44*a721d9a9SManos Pitsidianakis #[derive(Object)] 45*a721d9a9SManos Pitsidianakis struct Foo { 46*a721d9a9SManos Pitsidianakis _unused: [u8; 0], 47*a721d9a9SManos Pitsidianakis } 48*a721d9a9SManos Pitsidianakis }, 49*a721d9a9SManos Pitsidianakis "#[repr(C)] required for #[derive(Object)]" 50*a721d9a9SManos Pitsidianakis ); 51*a721d9a9SManos Pitsidianakis derive_compile!( 52*a721d9a9SManos Pitsidianakis derive_object_or_error, 53*a721d9a9SManos Pitsidianakis quote! { 54*a721d9a9SManos Pitsidianakis #[derive(Object)] 55*a721d9a9SManos Pitsidianakis #[repr(C)] 56*a721d9a9SManos Pitsidianakis struct Foo { 57*a721d9a9SManos Pitsidianakis _unused: [u8; 0], 58*a721d9a9SManos Pitsidianakis } 59*a721d9a9SManos Pitsidianakis }, 60*a721d9a9SManos Pitsidianakis quote! { 61*a721d9a9SManos Pitsidianakis ::qemu_api::assert_field_type!( 62*a721d9a9SManos Pitsidianakis Foo, 63*a721d9a9SManos Pitsidianakis _unused, 64*a721d9a9SManos Pitsidianakis ::qemu_api::qom::ParentField<<Foo as ::qemu_api::qom::ObjectImpl>::ParentType> 65*a721d9a9SManos Pitsidianakis ); 66*a721d9a9SManos Pitsidianakis ::qemu_api::module_init! { 67*a721d9a9SManos Pitsidianakis MODULE_INIT_QOM => unsafe { 68*a721d9a9SManos Pitsidianakis ::qemu_api::bindings::type_register_static(&<Foo as ::qemu_api::qom::ObjectImpl>::TYPE_INFO); 69*a721d9a9SManos Pitsidianakis } 70*a721d9a9SManos Pitsidianakis } 71*a721d9a9SManos Pitsidianakis } 72*a721d9a9SManos Pitsidianakis ); 73*a721d9a9SManos Pitsidianakis } 74*a721d9a9SManos Pitsidianakis 75*a721d9a9SManos Pitsidianakis #[test] 76*a721d9a9SManos Pitsidianakis fn test_derive_tryinto() { 77*a721d9a9SManos Pitsidianakis derive_compile_fail!( 78*a721d9a9SManos Pitsidianakis derive_tryinto_or_error, 79*a721d9a9SManos Pitsidianakis quote! { 80*a721d9a9SManos Pitsidianakis #[derive(TryInto)] 81*a721d9a9SManos Pitsidianakis struct Foo { 82*a721d9a9SManos Pitsidianakis _unused: [u8; 0], 83*a721d9a9SManos Pitsidianakis } 84*a721d9a9SManos Pitsidianakis }, 85*a721d9a9SManos Pitsidianakis "#[repr(u8/u16/u32/u64) required for #[derive(TryInto)]" 86*a721d9a9SManos Pitsidianakis ); 87*a721d9a9SManos Pitsidianakis derive_compile!( 88*a721d9a9SManos Pitsidianakis derive_tryinto_or_error, 89*a721d9a9SManos Pitsidianakis quote! { 90*a721d9a9SManos Pitsidianakis #[derive(TryInto)] 91*a721d9a9SManos Pitsidianakis #[repr(u8)] 92*a721d9a9SManos Pitsidianakis enum Foo { 93*a721d9a9SManos Pitsidianakis First = 0, 94*a721d9a9SManos Pitsidianakis Second, 95*a721d9a9SManos Pitsidianakis } 96*a721d9a9SManos Pitsidianakis }, 97*a721d9a9SManos Pitsidianakis quote! { 98*a721d9a9SManos Pitsidianakis impl Foo { 99*a721d9a9SManos Pitsidianakis #[allow(dead_code)] 100*a721d9a9SManos Pitsidianakis pub const fn into_bits(self) -> u8 { 101*a721d9a9SManos Pitsidianakis self as u8 102*a721d9a9SManos Pitsidianakis } 103*a721d9a9SManos Pitsidianakis 104*a721d9a9SManos Pitsidianakis #[allow(dead_code)] 105*a721d9a9SManos Pitsidianakis pub const fn from_bits(value: u8) -> Self { 106*a721d9a9SManos Pitsidianakis match ({ 107*a721d9a9SManos Pitsidianakis const First: u8 = Foo::First as u8; 108*a721d9a9SManos Pitsidianakis const Second: u8 = Foo::Second as u8; 109*a721d9a9SManos Pitsidianakis match value { 110*a721d9a9SManos Pitsidianakis First => core::result::Result::Ok(Foo::First), 111*a721d9a9SManos Pitsidianakis Second => core::result::Result::Ok(Foo::Second), 112*a721d9a9SManos Pitsidianakis _ => core::result::Result::Err(value), 113*a721d9a9SManos Pitsidianakis } 114*a721d9a9SManos Pitsidianakis }) { 115*a721d9a9SManos Pitsidianakis Ok(x) => x, 116*a721d9a9SManos Pitsidianakis Err(_) => panic!("invalid value for Foo"), 117*a721d9a9SManos Pitsidianakis } 118*a721d9a9SManos Pitsidianakis } 119*a721d9a9SManos Pitsidianakis } 120*a721d9a9SManos Pitsidianakis 121*a721d9a9SManos Pitsidianakis impl core::convert::TryFrom<u8> for Foo { 122*a721d9a9SManos Pitsidianakis type Error = u8; 123*a721d9a9SManos Pitsidianakis 124*a721d9a9SManos Pitsidianakis #[allow(ambiguous_associated_items)] 125*a721d9a9SManos Pitsidianakis fn try_from(value: u8) -> Result<Self, u8> { 126*a721d9a9SManos Pitsidianakis const First: u8 = Foo::First as u8; 127*a721d9a9SManos Pitsidianakis const Second: u8 = Foo::Second as u8; 128*a721d9a9SManos Pitsidianakis match value { 129*a721d9a9SManos Pitsidianakis First => core::result::Result::Ok(Foo::First), 130*a721d9a9SManos Pitsidianakis Second => core::result::Result::Ok(Foo::Second), 131*a721d9a9SManos Pitsidianakis _ => core::result::Result::Err(value), 132*a721d9a9SManos Pitsidianakis } 133*a721d9a9SManos Pitsidianakis } 134*a721d9a9SManos Pitsidianakis } 135*a721d9a9SManos Pitsidianakis } 136*a721d9a9SManos Pitsidianakis ); 137*a721d9a9SManos Pitsidianakis } 138