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