Given a TokenStream, can I produce a new TokenStream with all the attributes pre-expanded?
I suspect this would dramatically increase compile time, or require multiple compile steps.
I've tried to take a stab at writing a proc macro that can execute a closure on a function exit. The exact implementation looks like the following
#[proc_macro_attribute]
pub fn inspect(attr: TokenStream, item: TokenStream) -> TokenStream {
let mut callback = parse_macro_input!(attr as ExprClosure);
let function = parse_macro_input!(item as ItemFn);
let sig = &function.sig;
let body = &function.block;
if let Some(compile_error) = validate_inspect(&callback, &function) {
return compile_error;
}
let out = if sig.asyncness.is_some() {
quote! { let out = async move { #body }.await; }
} else {
quote! { let out = (move || { #body })(); }
};
quote! {
#sig {
#out
(#callback)(&out);
out
}
}
.into()
}
It may be used as follows:
#[inspect(|out| println!("increment returns {i}"))]
async fn increment(value: i32) -> i32 {
value + 1
}
The problem is that this does not play well with async_trait as the proc_macro for async_trait removes the asyncness of the function.
#[async_trait]
trait Foo {
async fn foo() -> i32;
}
struct Bar;
/// ! Compile error
#[async_trait]
impl Foo for Bar {
#[inspect(|out| println!("foo returns {}", i))]
async fn foo() -> i32 {
42
}
}
The following example does not compile because proc-macros are expanded outside in.
Without my proc macro,
#[async_trait]
impl Foo for Bar {
async fn foo() -> i32 {
42
}
}
async_trait expands to roughly something like this1:
impl Foo for Bar {
fn foo<'async_trait>() -> Pin<
Box<dyn Future<Output = i32> + Send + 'async_trait>,
> {
Box::pin(async move {
42
})
}
}
As you can see the function is converted to a synchronous function returning a dynamic future since async traits are not yet stabilized.
I would like to have a proc macro called invert_expansion_trait which expands the attributes of items inside a trait implementation first, before running it's own subsequent attributes. Would this be possible to implement today?
I tried creating a eager proc macro expansion that expands the attributes of an ItemImpl but couldn't get it to work because I lack knowledge / resources to understand the expansion process.
1: some code removed from expansion for ease of viewing
Per @chayim-friedman, I went with option 1, to eagerly inspect and expand the proc macros manually. It looks like this: