master
rs 515 lines 15.9 KB
Raw
1 // Copyright 2024, Linaro Limited
2 // Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
3 // SPDX-License-Identifier: GPL-2.0-or-later
4
5 use proc_macro::TokenStream;
6 use quote::{quote, quote_spanned};
7 use syn::{
8 parse::{Parse, ParseStream},
9 parse_macro_input, parse_quote,
10 punctuated::Punctuated,
11 spanned::Spanned,
12 token::Comma,
13 Attribute, Data, DeriveInput, Error, Field, Fields, FieldsUnnamed, Ident, Meta, Path, Token,
14 Variant,
15 };
16
17 mod bits;
18 use bits::BitsConstInternal;
19
20 mod migration_state;
21 use migration_state::MigrationStateDerive;
22
23 #[cfg(test)]
24 mod tests;
25
26 fn get_fields<'a>(
27 input: &'a DeriveInput,
28 msg: &str,
29 ) -> Result<&'a Punctuated<Field, Comma>, Error> {
30 let Data::Struct(ref s) = &input.data else {
31 return Err(Error::new(
32 input.ident.span(),
33 format!("Struct required for {msg}"),
34 ));
35 };
36 let Fields::Named(ref fs) = &s.fields else {
37 return Err(Error::new(
38 input.ident.span(),
39 format!("Named fields required for {msg}"),
40 ));
41 };
42 Ok(&fs.named)
43 }
44
45 fn get_unnamed_field<'a>(input: &'a DeriveInput, msg: &str) -> Result<&'a Field, Error> {
46 let Data::Struct(ref s) = &input.data else {
47 return Err(Error::new(
48 input.ident.span(),
49 format!("Struct required for {msg}"),
50 ));
51 };
52 let Fields::Unnamed(FieldsUnnamed { ref unnamed, .. }) = &s.fields else {
53 return Err(Error::new(
54 s.fields.span(),
55 format!("Tuple struct required for {msg}"),
56 ));
57 };
58 if unnamed.len() != 1 {
59 return Err(Error::new(
60 s.fields.span(),
61 format!("A single field is required for {msg}"),
62 ));
63 }
64 Ok(&unnamed[0])
65 }
66
67 fn is_c_repr(input: &DeriveInput, msg: &str) -> Result<(), Error> {
68 let expected = parse_quote! { #[repr(C)] };
69
70 if input.attrs.iter().any(|attr| attr == &expected) {
71 Ok(())
72 } else {
73 Err(Error::new(
74 input.ident.span(),
75 format!("#[repr(C)] required for {msg}"),
76 ))
77 }
78 }
79
80 fn is_transparent_repr(input: &DeriveInput, msg: &str) -> Result<(), Error> {
81 let expected = parse_quote! { #[repr(transparent)] };
82
83 if input.attrs.iter().any(|attr| attr == &expected) {
84 Ok(())
85 } else {
86 Err(Error::new(
87 input.ident.span(),
88 format!("#[repr(transparent)] required for {msg}"),
89 ))
90 }
91 }
92
93 fn derive_object_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream, Error> {
94 is_c_repr(&input, "#[derive(Object)]")?;
95
96 let name = &input.ident;
97 let parent = &get_fields(&input, "#[derive(Object)]")?
98 .get(0)
99 .ok_or_else(|| {
100 Error::new(
101 input.ident.span(),
102 "#[derive(Object)] requires a parent field",
103 )
104 })?
105 .ident;
106
107 Ok(quote! {
108 ::common::assert_field_type!(#name, #parent,
109 ::qom::ParentField<<#name as ::qom::ObjectImpl>::ParentType>);
110
111 ::util::module_init! {
112 MODULE_INIT_QOM => unsafe {
113 ::qom::type_register_static(&<#name as ::qom::ObjectImpl>::TYPE_INFO);
114 }
115 }
116 })
117 }
118
119 #[proc_macro_derive(Object)]
120 pub fn derive_object(input: TokenStream) -> TokenStream {
121 let input = parse_macro_input!(input as DeriveInput);
122
123 derive_object_or_error(input)
124 .unwrap_or_else(syn::Error::into_compile_error)
125 .into()
126 }
127
128 fn derive_opaque_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream, Error> {
129 is_transparent_repr(&input, "#[derive(Wrapper)]")?;
130
131 let name = &input.ident;
132 let field = &get_unnamed_field(&input, "#[derive(Wrapper)]")?;
133 let typ = &field.ty;
134
135 Ok(quote! {
136 unsafe impl ::common::opaque::Wrapper for #name {
137 type Wrapped = <#typ as ::common::opaque::Wrapper>::Wrapped;
138 }
139 impl #name {
140 pub unsafe fn from_raw<'a>(ptr: *mut <Self as ::common::opaque::Wrapper>::Wrapped) -> &'a Self {
141 let ptr = ::std::ptr::NonNull::new(ptr).unwrap().cast::<Self>();
142 unsafe { ptr.as_ref() }
143 }
144
145 pub const fn as_mut_ptr(&self) -> *mut <Self as ::common::opaque::Wrapper>::Wrapped {
146 self.0.as_mut_ptr()
147 }
148
149 pub const fn as_ptr(&self) -> *const <Self as ::common::opaque::Wrapper>::Wrapped {
150 self.0.as_ptr()
151 }
152
153 pub const fn as_void_ptr(&self) -> *mut ::core::ffi::c_void {
154 self.0.as_void_ptr()
155 }
156
157 pub const fn raw_get(slot: *mut Self) -> *mut <Self as ::common::opaque::Wrapper>::Wrapped {
158 slot.cast()
159 }
160 }
161 })
162 }
163
164 #[derive(Debug)]
165 enum DevicePropertyName {
166 CStr(syn::LitCStr),
167 Str(syn::LitStr),
168 }
169
170 impl Parse for DevicePropertyName {
171 fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
172 let lo = input.lookahead1();
173 if lo.peek(syn::LitStr) {
174 Ok(Self::Str(input.parse()?))
175 } else if lo.peek(syn::LitCStr) {
176 Ok(Self::CStr(input.parse()?))
177 } else {
178 Err(lo.error())
179 }
180 }
181 }
182
183 #[derive(Default, Debug)]
184 struct DeviceProperty {
185 rename: Option<DevicePropertyName>,
186 bitnr: Option<syn::Expr>,
187 defval: Option<syn::Expr>,
188 }
189
190 impl DeviceProperty {
191 fn parse_from(&mut self, a: &Attribute) -> syn::Result<()> {
192 use attrs::{set, with, Attrs};
193 let mut parser = Attrs::new();
194 parser.once("rename", with::eq(set::parse(&mut self.rename)));
195 parser.once("bit", with::eq(set::parse(&mut self.bitnr)));
196 parser.once("default", with::eq(set::parse(&mut self.defval)));
197 a.parse_args_with(&mut parser)
198 }
199
200 fn parse(a: &Attribute) -> syn::Result<Self> {
201 let mut retval = Self::default();
202 retval.parse_from(a)?;
203 Ok(retval)
204 }
205 }
206
207 #[proc_macro_derive(Device, attributes(property))]
208 pub fn derive_device(input: TokenStream) -> TokenStream {
209 let input = parse_macro_input!(input as DeriveInput);
210
211 derive_device_or_error(input)
212 .unwrap_or_else(syn::Error::into_compile_error)
213 .into()
214 }
215
216 fn derive_device_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream, Error> {
217 is_c_repr(&input, "#[derive(Device)]")?;
218 let properties: Vec<(syn::Field, DeviceProperty)> = get_fields(&input, "#[derive(Device)]")?
219 .iter()
220 .flat_map(|f| {
221 f.attrs
222 .iter()
223 .filter(|a| a.path().is_ident("property"))
224 .map(|a| Ok((f.clone(), DeviceProperty::parse(a)?)))
225 })
226 .collect::<Result<Vec<_>, Error>>()?;
227 let name = &input.ident;
228 let mut properties_expanded = vec![];
229
230 for (field, prop) in properties {
231 let DeviceProperty {
232 rename,
233 bitnr,
234 defval,
235 } = prop;
236 let field_name = field.ident.unwrap();
237 macro_rules! str_to_c_str {
238 ($value:expr, $span:expr) => {{
239 let (value, span) = ($value, $span);
240 let cstr = std::ffi::CString::new(value.as_str()).map_err(|err| {
241 Error::new(
242 span,
243 format!(
244 "Property name `{value}` cannot be represented as a C string: {err}"
245 ),
246 )
247 })?;
248 let cstr_lit = syn::LitCStr::new(&cstr, span);
249 Ok(quote! { #cstr_lit })
250 }};
251 }
252
253 let prop_name = rename.map_or_else(
254 || str_to_c_str!(field_name.to_string(), field_name.span()),
255 |prop_rename| -> Result<proc_macro2::TokenStream, Error> {
256 match prop_rename {
257 DevicePropertyName::CStr(cstr_lit) => Ok(quote! { #cstr_lit }),
258 DevicePropertyName::Str(str_lit) => {
259 str_to_c_str!(str_lit.value(), str_lit.span())
260 }
261 }
262 },
263 )?;
264 let field_ty = field.ty.clone();
265 let (qdev_prop, bitval) = if let Some(bitval) = bitnr {
266 (
267 quote! { <#field_ty as ::hwcore::QDevProp>::BIT_INFO },
268 quote! {
269 {
270 const {
271 assert!(#bitval >= 0 && #bitval < #field_ty::BITS as _,
272 "bit number exceeds type bits range");
273 }
274 #bitval as u8
275 }
276 },
277 )
278 } else {
279 (
280 quote! { <#field_ty as ::hwcore::QDevProp>::BASE_INFO },
281 quote! { 0 },
282 )
283 };
284 let set_default = defval.is_some();
285 let defval = defval.unwrap_or(syn::Expr::Verbatim(quote! { 0 }));
286 properties_expanded.push(quote! {
287 ::hwcore::bindings::Property {
288 name: ::std::ffi::CStr::as_ptr(#prop_name),
289 info: #qdev_prop,
290 offset: ::core::mem::offset_of!(#name, #field_name) as isize,
291 bitnr: #bitval,
292 set_default: #set_default,
293 defval: ::hwcore::bindings::Property__bindgen_ty_1 { u: #defval as u64 },
294 ..::common::Zeroable::ZERO
295 }
296 });
297 }
298
299 Ok(quote_spanned! {input.span() =>
300 unsafe impl ::hwcore::DevicePropertiesImpl for #name {
301 const PROPERTIES: &'static [::hwcore::bindings::Property] = &[
302 #(#properties_expanded),*
303 ];
304 }
305 })
306 }
307
308 #[proc_macro_derive(Wrapper)]
309 pub fn derive_opaque(input: TokenStream) -> TokenStream {
310 let input = parse_macro_input!(input as DeriveInput);
311
312 derive_opaque_or_error(input)
313 .unwrap_or_else(syn::Error::into_compile_error)
314 .into()
315 }
316
317 #[allow(non_snake_case)]
318 fn get_repr_uN(input: &DeriveInput, msg: &str) -> Result<Path, Error> {
319 let repr = input.attrs.iter().find(|attr| attr.path().is_ident("repr"));
320 if let Some(repr) = repr {
321 let nested = repr.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
322 for meta in nested {
323 match meta {
324 Meta::Path(path) if path.is_ident("u8") => return Ok(path),
325 Meta::Path(path) if path.is_ident("u16") => return Ok(path),
326 Meta::Path(path) if path.is_ident("u32") => return Ok(path),
327 Meta::Path(path) if path.is_ident("u64") => return Ok(path),
328 _ => {}
329 }
330 }
331 }
332
333 Err(Error::new(
334 input.ident.span(),
335 format!("#[repr(u8/u16/u32/u64) required for {msg}"),
336 ))
337 }
338
339 fn get_variants(input: &DeriveInput) -> Result<&Punctuated<Variant, Comma>, Error> {
340 let Data::Enum(ref e) = &input.data else {
341 return Err(Error::new(
342 input.ident.span(),
343 "Cannot derive TryInto for union or struct.",
344 ));
345 };
346 if let Some(v) = e.variants.iter().find(|v| v.fields != Fields::Unit) {
347 return Err(Error::new(
348 v.fields.span(),
349 "Cannot derive TryInto for enum with non-unit variants.",
350 ));
351 }
352 Ok(&e.variants)
353 }
354
355 #[rustfmt::skip::macros(quote)]
356 fn derive_tryinto_body(
357 name: &Ident,
358 variants: &Punctuated<Variant, Comma>,
359 repr: &Path,
360 ) -> Result<proc_macro2::TokenStream, Error> {
361 let discriminants: Vec<&Ident> = variants.iter().map(|f| &f.ident).collect();
362
363 Ok(quote! {
364 #(const #discriminants: #repr = #name::#discriminants as #repr;)*
365 match value {
366 #(#discriminants => core::result::Result::Ok(#name::#discriminants),)*
367 _ => core::result::Result::Err(value),
368 }
369 })
370 }
371
372 #[rustfmt::skip::macros(quote)]
373 fn derive_tryinto_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream, Error> {
374 let repr = get_repr_uN(&input, "#[derive(TryInto)]")?;
375 let name = &input.ident;
376 let body = derive_tryinto_body(name, get_variants(&input)?, &repr)?;
377 let errmsg = format!("invalid value for {name}");
378
379 Ok(quote! {
380 impl #name {
381 #[allow(dead_code)]
382 pub const fn into_bits(self) -> #repr {
383 self as #repr
384 }
385
386 #[allow(dead_code)]
387 pub const fn from_bits(value: #repr) -> Self {
388 match ({
389 #body
390 }) {
391 Ok(x) => x,
392 Err(_) => panic!(#errmsg),
393 }
394 }
395 }
396 impl core::convert::TryFrom<#repr> for #name {
397 type Error = #repr;
398
399 #[allow(ambiguous_associated_items)]
400 fn try_from(value: #repr) -> Result<Self, #repr> {
401 #body
402 }
403 }
404 })
405 }
406
407 #[proc_macro_derive(TryInto)]
408 pub fn derive_tryinto(input: TokenStream) -> TokenStream {
409 let input = parse_macro_input!(input as DeriveInput);
410
411 derive_tryinto_or_error(input)
412 .unwrap_or_else(syn::Error::into_compile_error)
413 .into()
414 }
415
416 #[proc_macro]
417 pub fn bits_const_internal(ts: TokenStream) -> TokenStream {
418 let ts = proc_macro2::TokenStream::from(ts);
419 let mut it = ts.into_iter();
420
421 let out = BitsConstInternal::parse(&mut it).unwrap_or_else(syn::Error::into_compile_error);
422
423 // https://github.com/rust-lang/rust-clippy/issues/15852
424 quote! {
425 {
426 #[allow(clippy::double_parens)]
427 #out
428 }
429 }
430 .into()
431 }
432
433 /// Derive macro for generating migration state structures and trait
434 /// implementations.
435 ///
436 /// This macro generates a migration state struct and implements the
437 /// `ToMigrationState` trait for the annotated struct, enabling state
438 /// serialization and restoration. Note that defining a `VMStateDescription`
439 /// for the migration state struct is left to the user.
440 ///
441 /// # Container attributes
442 ///
443 /// The following attributes can be applied to the struct:
444 ///
445 /// - `#[migration_state(rename = CustomName)]` - Customizes the name of the
446 /// generated migration struct. By default, the generated struct is named
447 /// `{OriginalName}Migration`.
448 ///
449 /// # Field attributes
450 ///
451 /// The following attributes can be applied to individual fields:
452 ///
453 /// - `#[migration_state(omit)]` - Excludes the field from the migration state
454 /// entirely.
455 ///
456 /// - `#[migration_state(into(Type))]` - Converts the field using `.into()`
457 /// during both serialization and restoration.
458 ///
459 /// - `#[migration_state(try_into(Type))]` - Converts the field using
460 /// `.try_into()` during both serialization and restoration. Returns
461 /// `InvalidError` on conversion failure.
462 ///
463 /// - `#[migration_state(clone)]` - Clones the field value.
464 ///
465 /// Fields without any attributes use `ToMigrationState` recursively; note that
466 /// this is a simple copy for types that implement `Copy`.
467 ///
468 /// # Attribute compatibility
469 ///
470 /// - `omit` cannot be used with any other attributes
471 /// - only one of `into(Type)`, `try_into(Type)` can be used, but they can be
472 /// coupled with `clone`.
473 ///
474 /// # Examples
475 ///
476 /// Basic usage:
477 /// ```ignore
478 /// #[derive(ToMigrationState)]
479 /// struct MyStruct {
480 /// field1: u32,
481 /// field2: Timer,
482 /// }
483 /// ```
484 ///
485 /// With attributes:
486 /// ```ignore
487 /// #[derive(ToMigrationState)]
488 /// #[migration_state(rename = CustomMigration)]
489 /// struct MyStruct {
490 /// #[migration_state(omit)]
491 /// runtime_field: u32,
492 ///
493 /// #[migration_state(clone)]
494 /// shared_data: String,
495 ///
496 /// #[migration_state(into(Cow<'static, str>), clone)]
497 /// converted_field: String,
498 ///
499 /// #[migration_state(try_into(i8))]
500 /// fallible_field: u32,
501 ///
502 /// // Default: use ToMigrationState trait recursively
503 /// nested_field: NestedStruct,
504 ///
505 /// // Primitive types have a default implementation of ToMigrationState
506 /// simple_field: u32,
507 /// }
508 /// ```
509 #[proc_macro_derive(ToMigrationState, attributes(migration_state))]
510 pub fn derive_to_migration_state(input: TokenStream) -> TokenStream {
511 let input = parse_macro_input!(input as DeriveInput);
512 MigrationStateDerive::expand(input)
513 .unwrap_or_else(syn::Error::into_compile_error)
514 .into()
515 }