rust: qemu-macros: switch #[property] parsing to use combinators
Since we are going to add more attribute parsing for high-level migration state macros, use the attrs crate instead of a handwritten parser for device properties as well. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
193f2ab6e0
commit
60c96a8775
3 changed files with 49 additions and 56 deletions
11
rust/Cargo.lock
generated
11
rust/Cargo.lock
generated
|
|
@ -14,6 +14,16 @@ version = "1.2.7"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c84fc003e338a6f69fbd4f7fe9f92b535ff13e9af8997f3b14b6ddff8b1df46d"
|
||||
|
||||
[[package]]
|
||||
name = "attrs"
|
||||
version = "0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a207d40f43de65285f3de0509bb6cb16bc46098864fce957122bbacce327e5f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bilge"
|
||||
version = "0.2.0"
|
||||
|
|
@ -188,6 +198,7 @@ dependencies = [
|
|||
name = "qemu_macros"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"attrs",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
|
|
|
|||
|
|
@ -3,10 +3,14 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use quote::{quote, quote_spanned, ToTokens};
|
||||
use quote::{quote, quote_spanned};
|
||||
use syn::{
|
||||
parse::Parse, parse_macro_input, parse_quote, punctuated::Punctuated, spanned::Spanned,
|
||||
token::Comma, Data, DeriveInput, Error, Field, Fields, FieldsUnnamed, Ident, Meta, Path, Token,
|
||||
parse::{Parse, ParseStream},
|
||||
parse_macro_input, parse_quote,
|
||||
punctuated::Punctuated,
|
||||
spanned::Spanned,
|
||||
token::Comma,
|
||||
Attribute, Data, DeriveInput, Error, Field, Fields, FieldsUnnamed, Ident, Meta, Path, Token,
|
||||
Variant,
|
||||
};
|
||||
mod bits;
|
||||
|
|
@ -159,61 +163,37 @@ enum DevicePropertyName {
|
|||
Str(syn::LitStr),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
impl Parse for DevicePropertyName {
|
||||
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
|
||||
let lo = input.lookahead1();
|
||||
if lo.peek(syn::LitStr) {
|
||||
Ok(Self::Str(input.parse()?))
|
||||
} else if lo.peek(syn::LitCStr) {
|
||||
Ok(Self::CStr(input.parse()?))
|
||||
} else {
|
||||
Err(lo.error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
struct DeviceProperty {
|
||||
rename: Option<DevicePropertyName>,
|
||||
defval: Option<syn::Expr>,
|
||||
}
|
||||
|
||||
impl Parse for DeviceProperty {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let _: syn::Token![#] = input.parse()?;
|
||||
let bracketed;
|
||||
_ = syn::bracketed!(bracketed in input);
|
||||
let attribute = bracketed.parse::<syn::Ident>()?;
|
||||
debug_assert_eq!(&attribute.to_string(), "property");
|
||||
let mut retval = Self {
|
||||
rename: None,
|
||||
defval: None,
|
||||
};
|
||||
let content;
|
||||
_ = syn::parenthesized!(content in bracketed);
|
||||
while !content.is_empty() {
|
||||
let value: syn::Ident = content.parse()?;
|
||||
if value == "rename" {
|
||||
let _: syn::Token![=] = content.parse()?;
|
||||
if retval.rename.is_some() {
|
||||
return Err(syn::Error::new(
|
||||
value.span(),
|
||||
"`rename` can only be used at most once",
|
||||
));
|
||||
}
|
||||
if content.peek(syn::LitStr) {
|
||||
retval.rename = Some(DevicePropertyName::Str(content.parse::<syn::LitStr>()?));
|
||||
} else {
|
||||
retval.rename =
|
||||
Some(DevicePropertyName::CStr(content.parse::<syn::LitCStr>()?));
|
||||
}
|
||||
} else if value == "default" {
|
||||
let _: syn::Token![=] = content.parse()?;
|
||||
if retval.defval.is_some() {
|
||||
return Err(syn::Error::new(
|
||||
value.span(),
|
||||
"`default` can only be used at most once",
|
||||
));
|
||||
}
|
||||
retval.defval = Some(content.parse()?);
|
||||
} else {
|
||||
return Err(syn::Error::new(
|
||||
value.span(),
|
||||
format!("unrecognized field `{value}`"),
|
||||
));
|
||||
}
|
||||
impl DeviceProperty {
|
||||
fn parse_from(&mut self, a: &Attribute) -> syn::Result<()> {
|
||||
use attrs::{set, with, Attrs};
|
||||
let mut parser = Attrs::new();
|
||||
parser.once("rename", with::eq(set::parse(&mut self.rename)));
|
||||
parser.once("default", with::eq(set::parse(&mut self.defval)));
|
||||
a.parse_args_with(&mut parser)
|
||||
}
|
||||
|
||||
if !content.is_empty() {
|
||||
let _: syn::Token![,] = content.parse()?;
|
||||
}
|
||||
}
|
||||
fn parse(a: &Attribute) -> syn::Result<Self> {
|
||||
let mut retval = Self::default();
|
||||
retval.parse_from(a)?;
|
||||
Ok(retval)
|
||||
}
|
||||
}
|
||||
|
|
@ -235,7 +215,7 @@ fn derive_device_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream
|
|||
f.attrs
|
||||
.iter()
|
||||
.filter(|a| a.path().is_ident("property"))
|
||||
.map(|a| Ok((f.clone(), syn::parse2(a.to_token_stream())?)))
|
||||
.map(|a| Ok((f.clone(), DeviceProperty::parse(a)?)))
|
||||
})
|
||||
.collect::<Result<Vec<_>, Error>>()?;
|
||||
let name = &input.ident;
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ fn test_derive_device() {
|
|||
migrate_clock: bool,
|
||||
}
|
||||
},
|
||||
"unrecognized field `defalt`"
|
||||
"Expected one of `default` or `rename`"
|
||||
);
|
||||
// Check that repeated attributes are not allowed:
|
||||
derive_compile_fail!(
|
||||
|
|
@ -73,7 +73,8 @@ fn test_derive_device() {
|
|||
migrate_clock: bool,
|
||||
}
|
||||
},
|
||||
"`rename` can only be used at most once"
|
||||
"Duplicate argument",
|
||||
"Already used here",
|
||||
);
|
||||
derive_compile_fail!(
|
||||
derive_device_or_error,
|
||||
|
|
@ -85,7 +86,8 @@ fn test_derive_device() {
|
|||
migrate_clock: bool,
|
||||
}
|
||||
},
|
||||
"`default` can only be used at most once"
|
||||
"Duplicate argument",
|
||||
"Already used here",
|
||||
);
|
||||
// Check that the field name is preserved when `rename` isn't used:
|
||||
derive_compile!(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue