I am writing a procedural macro which works fine, but I am having trouble reporting errors in an ergonomic way. Using panic!
"works" but is not elegant and does not present the error message to the user nicely.
I know that I can report good errors while parsing a TokenStream
, but I need to produce errors while traversing the AST after it has been parsed.
The macro invocation looks like this:
attr_test! {
#[bool]
FOO
}
And should output:
const FOO: bool = false;
This is the macro code:
extern crate proc_macro;
use quote::quote;
use syn::parse::{Parse, ParseStream, Result};
use syn::{Attribute, parse_macro_input, Ident, Meta};
struct AttrTest {
attributes: Vec<Attribute>,
name: Ident,
}
impl Parse for AttrTest {
fn parse(input: ParseStream) -> Result<Self> {
Ok(AttrTest {
attributes: input.call(Attribute::parse_outer)?,
name: input.parse()?,
})
}
}
#[proc_macro]
pub fn attr_test(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
let test: AttrTest = parse_macro_input!(tokens);
let name = test.name;
let first_att = test.attributes
.get(0)
.and_then(|att| att.parse_meta().ok());
if let Some(Meta::Word(ty)) = first_att {
if ty.to_string() != "bool" {
panic!("expected bool");
}
let output = quote! {
const #name: #ty = false;
};
output.into()
} else {
panic!("malformed or missing metadata")
}
}
I would like to produce an error if anything other than bool
is specified in the attribute. For example, input like this:
attr_test! {
#[something_else]
FOO
}
should result in something like:
error: expected bool
attr_test! {
#[something_else]
^^^^^^^^^^^^^^ expected bool
FOO
}
During parsing, there is a Result
, which has lots of useful information including a span
, so the resulting errors can highlight the exact parts of the macro call that have a problem. But once I'm traversing the AST, I can't see a good way to report errors.
How should this be done?
unwrap()
inty.span().unwrap()
is not anything withResult
/Option
, but a method for convertingproc_macro2::Span
toproc_macro::Span
. – Intuitivism