In a procedural macro attached to a main function, I am parsing the attributes passed as AttributeArgs
to the macro.
#[proc_macro_attribute]
pub fn macro(_meta: CompilerTokenStream, input: CompilerTokenStream) -> CompilerTokenStream {
let attrs = syn::parse_macro_input!(_meta as syn::AttributeArgs);
for nested_meta in attrs {
match nested_meta {
syn::NestedMeta::Meta(m) =>
println!("Parsed metadata: {:?}", &m.path().get_ident().unwrap().to_string()),
syn::NestedMeta::Lit(lit) => match lit {
syn::Lit::Str(ref l) => return report_literals_not_allowed(&l.value(), &lit),
syn::Lit::ByteStr(ref l) => return report_literals_not_allowed(&String::from_utf8_lossy(&l.value()), &lit),
syn::Lit::Byte(ref l) => return report_literals_not_allowed(&l.value().to_string(), &lit),
syn::Lit::Char(ref l) => return report_literals_not_allowed(&l.value().to_string(), &lit),
syn::Lit::Int(ref l) => return report_literals_not_allowed(&l.to_string(), &lit),
syn::Lit::Float(ref l) => return report_literals_not_allowed(&l.to_string(), &lit),
syn::Lit::Bool(ref l) => return report_literals_not_allowed(&l.value().to_string(), &lit),
syn::Lit::Verbatim(ref l) => return report_literals_not_allowed(&l.to_string(), &lit)
}
}
}
// Parses the function that this attribute is attached to
let func_res = syn::parse::<FunctionParser>(input);
if func_res.is_err() {
return quote! { fn main() {} }.into()
}
let func = func_res.ok().unwrap();
let sign = func.clone().sig;
let body = func.clone().block.stmts;
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
Handler::run().await;
});
let mut queries_tokens: Vec<TokenStream> = Vec::new();
wire_queries_to_execute(&mut queries_tokens);
// The final code wired in main()
quote! {
#[tokio::main]
async #sign {
{
#(#queries_tokens)*
}
#(#body)*
}
}.into()
}
When the user of this macro, makes an error inside main, I return this:
let func_res = syn::parse::<FunctionParser>(input);
if func_res.is_err() {
return quote! { fn main() {} }.into()
}
Rust analyzer and the compiler reports fine the errors on the code, and then a new main()
is wired again, so there's no error main function not found in crate. Please, consider add a main function...
which is the problem that I want to solve.
But, when an error is detected on the parsed attributes, I am not able to get rid out of that error. What I've tried so far is this:
pub fn report_literals_not_allowed(ident: &str, s: &Lit) -> TokenStream {
syn::Error::new_spanned(ident, s.span.into()),
"No literals allowed in the `macro` proc-macro"
).into_compile_error().into()
}
Which reports exactly where the user is passing a literal, which is a feature not allowed in the arguments of the attribute.
I've tried to solve it like this:
pub fn report_literals_not_allowed(ident: &str, s: &Lit) -> TokenStream {
let ident = Ident::new(ident, s.span().into());
syn::Error::new_spanned(
quote! {
#ident
fn main() {}
},
"No literals allowed in the `macro` proc-macro"
).into_compile_error().into()
}
but I still have the same error of no 'main' funcion found in crate...
How can I report the error of that the macro does not accept literal values, and remove
the error of not main found
at the same time?
syn::Error
just generates code that calls compile_error!()
. It is a normal TokenStream
. You just need to extend it with the code you want:
pub fn report_literals_not_allowed(ident: &str, s: &Lit) -> TokenStream {
let error = syn::Error::new_spanned(s, "No literals allowed in the `macro` proc-macro")
.into_compile_error();
quote! {
#error
fn main() {}
}
.into()
}