Hi there, I've tried to find some tutorial/blogs to learn on how to write a complex proc_macro but I'm not able to find one that goes as deep as I require.
I'd like to annotate a function like this:
struct bar;
#[MyAnnotation]
fn foo() {
let special = bar::new();
let c = special.handle();
}
The call in the function body special.handle()
shall be found by the proc macro and introduce some specific code that reduces to write lot's of boilerplate when implementing the same feature by hand on the structure bar
as this would be the same for an bar that would use this special handling.
What I get so far was writing a visitor that takes the AST provided in the pro_macro implementation to parse the function body that was annotated.
What I got so far:
#[proc_macro_attribute]
#[allow(non_snake_case)]
pub fn MyAnnotation(attr: TokenStream, item: TokenStream) -> TokenStream {
// get the function this attribute is attached to
let func = parse_macro_input!(item as ItemFn);
let block = &func.block;
// visit the function body to parse it
let mut test = FunctionBody{};
test.visit_block(block);
}
struct FunctionBody<'a> {}
impl<'a> Visit<'a> for FunctionBody<'a> {
/// while visiting the statements of the thinkable function body we can prepare how this will be
/// translated into the thinkable "state-machine"
fn visit_stmt(&mut self, s: &'a Stmt) {
match s {
// an expression without a semicolon, this is usually one that will provide the result
// of the function if it is the last expression
Stmt::Expr(expr) => {
println!("found expression");
},
// an expression with semicolon, this could be eg. a function/method call awaiting a thinkable
Stmt::Semi(expr, token) => {
println!("found semi-colon expression");
match expr {
// an "foo.handle;" is represented as Field expression where the Member is "handle"
Expr::Field( ExprField {
dot_token: Dot, //Token![.],
member: Member::Named(
ident
/*proc_macro2::Ident {
inner: proc_macro2::imp::Ident::Compiler(i),
..
}*/
),
..
}) => {
println!("found handle call? {:?}", ident);
},
Expr::Call(binding) => {
},
_ => (),
}
},
_ => ()
};
}
}
The issue is, that I cannot find a way to match on the identifier name in this match axpression:
match expr {
// an "foo.handle;" is represented as Field expression where the Member is "handle"
Expr::Field( ExprField {
dot_token: Dot, //Token![.],
member: Member::Named(
ident
/*proc_macro2::Ident {
inner: proc_macro2::imp::Ident::Compiler(i),
..
}*/
),
..
})
while printing the identifier to the console results in: found call? Ident { ident: "handle", span: #0 bytes(284..290) }
, so the identifier is there. But the inner structure of the proc_macro2::Ident is private - therefore the compiler complains when pattern matching the inner values. Is there anyone here experience with this deep level of proc_macro development and could help out here?
Thx in advance.