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]
test_derive_object()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]
test_derive_tryinto()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