Expand description
Make a parser that is usable with parse_macro_input!
in a
#[proc_macro_attribute]
macro.
Warning: When parsing attribute args other than the
proc_macro::TokenStream
input of a proc_macro_attribute
, you do not
need this function. In several cases your callers will get worse error
messages if you use this function, because the surrounding delimiter’s span
is concealed from attribute macros by rustc. Use
Attribute::parse_nested_meta
instead.
Example
This example implements an attribute macro whose invocations look like this:
#[tea(kind = "EarlGrey", hot)]
struct Picard {...}
The “parameters” supported by the attribute are:
kind = "..."
hot
with(sugar, milk, ...)
, a comma-separated list of ingredients
use proc_macro::TokenStream;
use syn::{parse_macro_input, LitStr, Path};
#[proc_macro_attribute]
pub fn tea(args: TokenStream, input: TokenStream) -> TokenStream {
let mut kind: Option<LitStr> = None;
let mut hot: bool = false;
let mut with: Vec<Path> = Vec::new();
let tea_parser = syn::meta::parser(|meta| {
if meta.path.is_ident("kind") {
kind = Some(meta.value()?.parse()?);
Ok(())
} else if meta.path.is_ident("hot") {
hot = true;
Ok(())
} else if meta.path.is_ident("with") {
meta.parse_nested_meta(|meta| {
with.push(meta.path);
Ok(())
})
} else {
Err(meta.error("unsupported tea property"))
}
});
parse_macro_input!(args with tea_parser);
eprintln!("kind={kind:?} hot={hot} with={with:?}");
/* ... */
}
The syn::meta
library will take care of dealing with the commas including
trailing commas, and producing sensible error messages on unexpected input.
error: expected `,`
--> src/main.rs:3:37
|
3 | #[tea(kind = "EarlGrey", with(sugar = "lol", milk))]
| ^
Example
Same as above but we factor out most of the logic into a separate function.
use proc_macro::TokenStream;
use syn::meta::ParseNestedMeta;
use syn::parse::{Parser, Result};
use syn::{parse_macro_input, LitStr, Path};
#[proc_macro_attribute]
pub fn tea(args: TokenStream, input: TokenStream) -> TokenStream {
let mut attrs = TeaAttributes::default();
let tea_parser = syn::meta::parser(|meta| attrs.parse(meta));
parse_macro_input!(args with tea_parser);
/* ... */
}
#[derive(Default)]
struct TeaAttributes {
kind: Option<LitStr>,
hot: bool,
with: Vec<Path>,
}
impl TeaAttributes {
fn parse(&mut self, meta: ParseNestedMeta) -> Result<()> {
if meta.path.is_ident("kind") {
self.kind = Some(meta.value()?.parse()?);
Ok(())
} else /* just like in last example */
}
}