From: Manos Pitsidianakis <manos.pitsidiana...@linaro.org>

Our MacroError type wraps syn::Error as a variant, and uses another
variant for custom errors. Fortunately syn::Error can be used directly,
avoiding extra code on our side, so change the proc macro crate to use
it.

Signed-off-by: Manos Pitsidianakis <manos.pitsidiana...@linaro.org>
Link: 
https://lore.kernel.org/r/20250703-rust_macros-v1-1-b99f82feb...@linaro.org
Signed-off-by: Paolo Bonzini <pbonz...@redhat.com>
---
 docs/devel/rust.rst               | 11 ++--
 rust/qemu-api-macros/src/bits.rs  | 58 ++++++++-------------
 rust/qemu-api-macros/src/lib.rs   | 86 +++++++++++++++----------------
 rust/qemu-api-macros/src/utils.rs | 26 ----------
 4 files changed, 70 insertions(+), 111 deletions(-)
 delete mode 100644 rust/qemu-api-macros/src/utils.rs

diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst
index dc8c44109e1..b6737536c69 100644
--- a/docs/devel/rust.rst
+++ b/docs/devel/rust.rst
@@ -351,7 +351,7 @@ Writing procedural macros
 '''''''''''''''''''''''''
 
 By conventions, procedural macros are split in two functions, one
-returning ``Result<proc_macro2::TokenStream, MacroError>`` with the body of
+returning ``Result<proc_macro2::TokenStream, syn::Error>`` with the body of
 the procedural macro, and the second returning ``proc_macro::TokenStream``
 which is the actual procedural macro.  The former's name is the same as
 the latter with the ``_or_error`` suffix.  The code for the latter is more
@@ -361,18 +361,19 @@ from the type after ``as`` in the invocation of 
``parse_macro_input!``::
     #[proc_macro_derive(Object)]
     pub fn derive_object(input: TokenStream) -> TokenStream {
         let input = parse_macro_input!(input as DeriveInput);
-        let expanded = 
derive_object_or_error(input).unwrap_or_else(Into::into);
 
-        TokenStream::from(expanded)
+        derive_object_or_error(input)
+            .unwrap_or_else(syn::Error::into_compile_error)
+            .into()
     }
 
 The ``qemu_api_macros`` crate has utility functions to examine a
 ``DeriveInput`` and perform common checks (e.g. looking for a struct
-with named fields).  These functions return ``Result<..., MacroError>``
+with named fields).  These functions return ``Result<..., syn::Error>``
 and can be used easily in the procedural macro function::
 
     fn derive_object_or_error(input: DeriveInput) ->
-        Result<proc_macro2::TokenStream, MacroError>
+        Result<proc_macro2::TokenStream, Error>
     {
         is_c_repr(&input, "#[derive(Object)]")?;
 
diff --git a/rust/qemu-api-macros/src/bits.rs b/rust/qemu-api-macros/src/bits.rs
index 5ba84757ee0..a80a3b9fee1 100644
--- a/rust/qemu-api-macros/src/bits.rs
+++ b/rust/qemu-api-macros/src/bits.rs
@@ -6,8 +6,7 @@
 use proc_macro2::{
     Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree, 
TokenTree as TT,
 };
-
-use crate::utils::MacroError;
+use syn::Error;
 
 pub struct BitsConstInternal {
     typ: TokenTree,
@@ -36,27 +35,21 @@ fn parse_primary(
         tok: TokenTree,
         it: &mut dyn Iterator<Item = TokenTree>,
         out: &mut TokenStream,
-    ) -> Result<Option<TokenTree>, MacroError> {
+    ) -> Result<Option<TokenTree>, Error> {
         let next = match tok {
             TT::Group(ref g) => {
                 if g.delimiter() != Delimiter::Parenthesis && g.delimiter() != 
Delimiter::None {
-                    return Err(MacroError::Message("expected 
parenthesis".into(), g.span()));
+                    return Err(Error::new(g.span(), "expected parenthesis"));
                 }
                 let mut stream = g.stream().into_iter();
                 let Some(first_tok) = stream.next() else {
-                    return Err(MacroError::Message(
-                        "expected operand, found ')'".into(),
-                        g.span(),
-                    ));
+                    return Err(Error::new(g.span(), "expected operand, found 
')'"));
                 };
                 let mut output = TokenStream::new();
                 // start from the lowest precedence
                 let next = self.parse_or(first_tok, &mut stream, &mut output)?;
                 if let Some(tok) = next {
-                    return Err(MacroError::Message(
-                        format!("unexpected token {tok}"),
-                        tok.span(),
-                    ));
+                    return Err(Error::new(tok.span(), format!("unexpected 
token {tok}")));
                 }
                 out.extend(Some(paren(output)));
                 it.next()
@@ -74,20 +67,17 @@ fn parse_primary(
             }
             TT::Punct(ref p) => {
                 if p.as_char() != '!' {
-                    return Err(MacroError::Message("expected operand".into(), 
p.span()));
+                    return Err(Error::new(p.span(), "expected operand"));
                 }
                 let Some(rhs_tok) = it.next() else {
-                    return Err(MacroError::Message(
-                        "expected operand at end of input".into(),
-                        p.span(),
-                    ));
+                    return Err(Error::new(p.span(), "expected operand at end 
of input"));
                 };
                 let next = self.parse_primary(rhs_tok, it, out)?;
                 out.extend([punct('.'), ident("invert"), 
paren(TokenStream::new())]);
                 next
             }
             _ => {
-                return Err(MacroError::Message("unexpected literal".into(), 
tok.span()));
+                return Err(Error::new(tok.span(), "unexpected literal"));
             }
         };
         Ok(next)
@@ -99,7 +89,7 @@ fn parse_binop<
             TokenTree,
             &mut dyn Iterator<Item = TokenTree>,
             &mut TokenStream,
-        ) -> Result<Option<TokenTree>, MacroError>,
+        ) -> Result<Option<TokenTree>, Error>,
     >(
         &self,
         tok: TokenTree,
@@ -108,7 +98,7 @@ fn parse_binop<
         ch: char,
         f: F,
         method: &'static str,
-    ) -> Result<Option<TokenTree>, MacroError> {
+    ) -> Result<Option<TokenTree>, Error> {
         let mut next = f(self, tok, it, out)?;
         while next.is_some() {
             let op = next.as_ref().unwrap();
@@ -118,10 +108,7 @@ fn parse_binop<
             }
 
             let Some(rhs_tok) = it.next() else {
-                return Err(MacroError::Message(
-                    "expected operand at end of input".into(),
-                    p.span(),
-                ));
+                return Err(Error::new(p.span(), "expected operand at end of 
input"));
             };
             let mut rhs = TokenStream::new();
             next = f(self, rhs_tok, it, &mut rhs)?;
@@ -136,7 +123,7 @@ pub fn parse_sub(
         tok: TokenTree,
         it: &mut dyn Iterator<Item = TokenTree>,
         out: &mut TokenStream,
-    ) -> Result<Option<TokenTree>, MacroError> {
+    ) -> Result<Option<TokenTree>, Error> {
         self.parse_binop(tok, it, out, '-', Self::parse_primary, "difference")
     }
 
@@ -146,7 +133,7 @@ fn parse_and(
         tok: TokenTree,
         it: &mut dyn Iterator<Item = TokenTree>,
         out: &mut TokenStream,
-    ) -> Result<Option<TokenTree>, MacroError> {
+    ) -> Result<Option<TokenTree>, Error> {
         self.parse_binop(tok, it, out, '&', Self::parse_sub, "intersection")
     }
 
@@ -156,7 +143,7 @@ fn parse_xor(
         tok: TokenTree,
         it: &mut dyn Iterator<Item = TokenTree>,
         out: &mut TokenStream,
-    ) -> Result<Option<TokenTree>, MacroError> {
+    ) -> Result<Option<TokenTree>, Error> {
         self.parse_binop(tok, it, out, '^', Self::parse_and, 
"symmetric_difference")
     }
 
@@ -166,13 +153,13 @@ pub fn parse_or(
         tok: TokenTree,
         it: &mut dyn Iterator<Item = TokenTree>,
         out: &mut TokenStream,
-    ) -> Result<Option<TokenTree>, MacroError> {
+    ) -> Result<Option<TokenTree>, Error> {
         self.parse_binop(tok, it, out, '|', Self::parse_xor, "union")
     }
 
     pub fn parse(
         it: &mut dyn Iterator<Item = TokenTree>,
-    ) -> Result<proc_macro2::TokenStream, MacroError> {
+    ) -> Result<proc_macro2::TokenStream, Error> {
         let mut pos = Span::call_site();
         let mut typ = proc_macro2::TokenStream::new();
 
@@ -198,15 +185,15 @@ pub fn parse(
         };
 
         let Some(tok) = next else {
-            return Err(MacroError::Message(
-                "expected expression, do not call this macro directly".into(),
+            return Err(Error::new(
                 pos,
+                "expected expression, do not call this macro directly",
             ));
         };
         let TT::Group(ref _group) = tok else {
-            return Err(MacroError::Message(
-                "expected parenthesis, do not call this macro directly".into(),
+            return Err(Error::new(
                 tok.span(),
+                "expected parenthesis, do not call this macro directly",
             ));
         };
         let mut out = TokenStream::new();
@@ -219,10 +206,7 @@ pub fn parse(
         // A parenthesized expression is a single production of the grammar,
         // so the input must have reached the last token.
         if let Some(tok) = next {
-            return Err(MacroError::Message(
-                format!("unexpected token {tok}"),
-                tok.span(),
-            ));
+            return Err(Error::new(tok.span(), format!("unexpected token 
{tok}")));
         }
         Ok(out)
     }
diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs
index c18bb4e036f..2cb79c799a2 100644
--- a/rust/qemu-api-macros/src/lib.rs
+++ b/rust/qemu-api-macros/src/lib.rs
@@ -6,83 +6,79 @@
 use quote::quote;
 use syn::{
     parse_macro_input, parse_quote, punctuated::Punctuated, spanned::Spanned, 
token::Comma, Data,
-    DeriveInput, Field, Fields, FieldsUnnamed, Ident, Meta, Path, Token, 
Variant,
+    DeriveInput, Error, Field, Fields, FieldsUnnamed, Ident, Meta, Path, 
Token, Variant,
 };
-
-mod utils;
-use utils::MacroError;
-
 mod bits;
 use bits::BitsConstInternal;
 
 fn get_fields<'a>(
     input: &'a DeriveInput,
     msg: &str,
-) -> Result<&'a Punctuated<Field, Comma>, MacroError> {
+) -> Result<&'a Punctuated<Field, Comma>, Error> {
     let Data::Struct(ref s) = &input.data else {
-        return Err(MacroError::Message(
-            format!("Struct required for {msg}"),
+        return Err(Error::new(
             input.ident.span(),
+            format!("Struct required for {msg}"),
         ));
     };
     let Fields::Named(ref fs) = &s.fields else {
-        return Err(MacroError::Message(
-            format!("Named fields required for {msg}"),
+        return Err(Error::new(
             input.ident.span(),
+            format!("Named fields required for {msg}"),
         ));
     };
     Ok(&fs.named)
 }
 
-fn get_unnamed_field<'a>(input: &'a DeriveInput, msg: &str) -> Result<&'a 
Field, MacroError> {
+fn get_unnamed_field<'a>(input: &'a DeriveInput, msg: &str) -> Result<&'a 
Field, Error> {
     let Data::Struct(ref s) = &input.data else {
-        return Err(MacroError::Message(
-            format!("Struct required for {msg}"),
+        return Err(Error::new(
             input.ident.span(),
+            format!("Struct required for {msg}"),
         ));
     };
     let Fields::Unnamed(FieldsUnnamed { ref unnamed, .. }) = &s.fields else {
-        return Err(MacroError::Message(
-            format!("Tuple struct required for {msg}"),
+        return Err(Error::new(
             s.fields.span(),
+            format!("Tuple struct required for {msg}"),
         ));
     };
     if unnamed.len() != 1 {
-        return Err(MacroError::Message(
-            format!("A single field is required for {msg}"),
+        return Err(Error::new(
             s.fields.span(),
+            format!("A single field is required for {msg}"),
         ));
     }
     Ok(&unnamed[0])
 }
 
-fn is_c_repr(input: &DeriveInput, msg: &str) -> Result<(), MacroError> {
+fn is_c_repr(input: &DeriveInput, msg: &str) -> Result<(), Error> {
     let expected = parse_quote! { #[repr(C)] };
 
     if input.attrs.iter().any(|attr| attr == &expected) {
         Ok(())
     } else {
-        Err(MacroError::Message(
-            format!("#[repr(C)] required for {msg}"),
+        Err(Error::new(
             input.ident.span(),
+            format!("#[repr(C)] required for {msg}"),
         ))
     }
 }
 
-fn is_transparent_repr(input: &DeriveInput, msg: &str) -> Result<(), 
MacroError> {
+fn is_transparent_repr(input: &DeriveInput, msg: &str) -> Result<(), Error> {
     let expected = parse_quote! { #[repr(transparent)] };
 
     if input.attrs.iter().any(|attr| attr == &expected) {
         Ok(())
     } else {
-        Err(MacroError::Message(
-            format!("#[repr(transparent)] required for {msg}"),
+        Err(Error::new(
             input.ident.span(),
+            format!("#[repr(transparent)] required for {msg}"),
         ))
     }
 }
 
-fn derive_object_or_error(input: DeriveInput) -> 
Result<proc_macro2::TokenStream, MacroError> {
+fn derive_object_or_error(input: DeriveInput) -> 
Result<proc_macro2::TokenStream, Error> {
     is_c_repr(&input, "#[derive(Object)]")?;
 
     let name = &input.ident;
@@ -103,12 +99,13 @@ fn derive_object_or_error(input: DeriveInput) -> 
Result<proc_macro2::TokenStream
 #[proc_macro_derive(Object)]
 pub fn derive_object(input: TokenStream) -> TokenStream {
     let input = parse_macro_input!(input as DeriveInput);
-    let expanded = derive_object_or_error(input).unwrap_or_else(Into::into);
 
-    TokenStream::from(expanded)
+    derive_object_or_error(input)
+        .unwrap_or_else(syn::Error::into_compile_error)
+        .into()
 }
 
-fn derive_opaque_or_error(input: DeriveInput) -> 
Result<proc_macro2::TokenStream, MacroError> {
+fn derive_opaque_or_error(input: DeriveInput) -> 
Result<proc_macro2::TokenStream, Error> {
     is_transparent_repr(&input, "#[derive(Wrapper)]")?;
 
     let name = &input.ident;
@@ -149,13 +146,14 @@ pub const fn raw_get(slot: *mut Self) -> *mut <Self as 
crate::cell::Wrapper>::Wr
 #[proc_macro_derive(Wrapper)]
 pub fn derive_opaque(input: TokenStream) -> TokenStream {
     let input = parse_macro_input!(input as DeriveInput);
-    let expanded = derive_opaque_or_error(input).unwrap_or_else(Into::into);
 
-    TokenStream::from(expanded)
+    derive_opaque_or_error(input)
+        .unwrap_or_else(syn::Error::into_compile_error)
+        .into()
 }
 
 #[allow(non_snake_case)]
-fn get_repr_uN(input: &DeriveInput, msg: &str) -> Result<Path, MacroError> {
+fn get_repr_uN(input: &DeriveInput, msg: &str) -> Result<Path, Error> {
     let repr = input.attrs.iter().find(|attr| attr.path().is_ident("repr"));
     if let Some(repr) = repr {
         let nested = repr.parse_args_with(Punctuated::<Meta, 
Token![,]>::parse_terminated)?;
@@ -170,23 +168,23 @@ fn get_repr_uN(input: &DeriveInput, msg: &str) -> 
Result<Path, MacroError> {
         }
     }
 
-    Err(MacroError::Message(
-        format!("#[repr(u8/u16/u32/u64) required for {msg}"),
+    Err(Error::new(
         input.ident.span(),
+        format!("#[repr(u8/u16/u32/u64) required for {msg}"),
     ))
 }
 
-fn get_variants(input: &DeriveInput) -> Result<&Punctuated<Variant, Comma>, 
MacroError> {
+fn get_variants(input: &DeriveInput) -> Result<&Punctuated<Variant, Comma>, 
Error> {
     let Data::Enum(ref e) = &input.data else {
-        return Err(MacroError::Message(
-            "Cannot derive TryInto for union or struct.".to_string(),
+        return Err(Error::new(
             input.ident.span(),
+            "Cannot derive TryInto for union or struct.",
         ));
     };
     if let Some(v) = e.variants.iter().find(|v| v.fields != Fields::Unit) {
-        return Err(MacroError::Message(
-            "Cannot derive TryInto for enum with non-unit 
variants.".to_string(),
+        return Err(Error::new(
             v.fields.span(),
+            "Cannot derive TryInto for enum with non-unit variants.",
         ));
     }
     Ok(&e.variants)
@@ -197,7 +195,7 @@ fn derive_tryinto_body(
     name: &Ident,
     variants: &Punctuated<Variant, Comma>,
     repr: &Path,
-) -> Result<proc_macro2::TokenStream, MacroError> {
+) -> Result<proc_macro2::TokenStream, Error> {
     let discriminants: Vec<&Ident> = variants.iter().map(|f| 
&f.ident).collect();
 
     Ok(quote! {
@@ -210,7 +208,7 @@ fn derive_tryinto_body(
 }
 
 #[rustfmt::skip::macros(quote)]
-fn derive_tryinto_or_error(input: DeriveInput) -> 
Result<proc_macro2::TokenStream, MacroError> {
+fn derive_tryinto_or_error(input: DeriveInput) -> 
Result<proc_macro2::TokenStream, Error> {
     let repr = get_repr_uN(&input, "#[derive(TryInto)]")?;
     let name = &input.ident;
     let body = derive_tryinto_body(name, get_variants(&input)?, &repr)?;
@@ -247,9 +245,10 @@ fn try_from(value: #repr) -> Result<Self, #repr> {
 #[proc_macro_derive(TryInto)]
 pub fn derive_tryinto(input: TokenStream) -> TokenStream {
     let input = parse_macro_input!(input as DeriveInput);
-    let expanded = derive_tryinto_or_error(input).unwrap_or_else(Into::into);
 
-    TokenStream::from(expanded)
+    derive_tryinto_or_error(input)
+        .unwrap_or_else(syn::Error::into_compile_error)
+        .into()
 }
 
 #[proc_macro]
@@ -257,6 +256,7 @@ pub fn bits_const_internal(ts: TokenStream) -> TokenStream {
     let ts = proc_macro2::TokenStream::from(ts);
     let mut it = ts.into_iter();
 
-    let expanded = BitsConstInternal::parse(&mut 
it).unwrap_or_else(Into::into);
-    TokenStream::from(expanded)
+    BitsConstInternal::parse(&mut it)
+        .unwrap_or_else(syn::Error::into_compile_error)
+        .into()
 }
diff --git a/rust/qemu-api-macros/src/utils.rs 
b/rust/qemu-api-macros/src/utils.rs
deleted file mode 100644
index 02c91aed7f6..00000000000
--- a/rust/qemu-api-macros/src/utils.rs
+++ /dev/null
@@ -1,26 +0,0 @@
-// Procedural macro utilities.
-// Author(s): Paolo Bonzini <pbonz...@redhat.com>
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-use proc_macro2::Span;
-use quote::quote_spanned;
-
-pub enum MacroError {
-    Message(String, Span),
-    ParseError(syn::Error),
-}
-
-impl From<syn::Error> for MacroError {
-    fn from(err: syn::Error) -> Self {
-        MacroError::ParseError(err)
-    }
-}
-
-impl From<MacroError> for proc_macro2::TokenStream {
-    fn from(err: MacroError) -> Self {
-        match err {
-            MacroError::Message(msg, span) => quote_spanned! { span => 
compile_error!(#msg); },
-            MacroError::ParseError(err) => err.into_compile_error(),
-        }
-    }
-}
-- 
2.50.0


Reply via email to