mirror of
https://github.com/xemu-project/xemu.git
synced 2026-07-11 01:24:41 +02:00
rust/qemu-api-macros: use syn::Error directly
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.pitsidianakis@linaro.org> Link: https://lore.kernel.org/r/20250703-rust_macros-v1-1-b99f82febbbf@linaro.org Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
committed by
Paolo Bonzini
parent
bffbb430ee
commit
caa08d30f2
+6
-5
@@ -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)]")?;
|
||||
|
||||
|
||||
@@ -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 @@ impl BitsConstInternal {
|
||||
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 @@ impl BitsConstInternal {
|
||||
}
|
||||
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 @@ impl BitsConstInternal {
|
||||
TokenTree,
|
||||
&mut dyn Iterator<Item = TokenTree>,
|
||||
&mut TokenStream,
|
||||
) -> Result<Option<TokenTree>, MacroError>,
|
||||
) -> Result<Option<TokenTree>, Error>,
|
||||
>(
|
||||
&self,
|
||||
tok: TokenTree,
|
||||
@@ -108,7 +98,7 @@ impl BitsConstInternal {
|
||||
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 @@ impl BitsConstInternal {
|
||||
}
|
||||
|
||||
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 @@ impl BitsConstInternal {
|
||||
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 @@ impl BitsConstInternal {
|
||||
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 @@ impl BitsConstInternal {
|
||||
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 @@ impl BitsConstInternal {
|
||||
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 @@ impl BitsConstInternal {
|
||||
};
|
||||
|
||||
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 @@ impl BitsConstInternal {
|
||||
// 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)
|
||||
}
|
||||
|
||||
@@ -6,83 +6,79 @@ use proc_macro::TokenStream;
|
||||
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 @@ fn derive_opaque_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream
|
||||
#[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 derive_tryinto_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStrea
|
||||
#[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()
|
||||
}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
// Procedural macro utilities.
|
||||
// Author(s): Paolo Bonzini <pbonzini@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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user