在函数开头注入代码的过程属性宏
Procedural attribute macro to inject code at the beginning of a function
我正在尝试构建一个简单的属性,它将在函数的开头注入一个 let 绑定,因此结果将是:
#[foo]
fn bar(){
// start injected code
let x = 0;
// end injected code
...
}
我已经做到了:
use proc_macro::TokenStream;
use quote::{quote, ToTokens};
use syn::{parse_macro_input, ItemFn, Item, Stmt};
#[proc_macro_attribute]
pub fn hello(attr: TokenStream, stream: TokenStream) -> TokenStream {
let input = parse_macro_input!(stream as ItemFn);
let block = input.block;
let res = quote! {let x = 1;};
// doesn't work, can't convert TokenStream into Stmt
block.stmts.insert(0, res.into());
TokenStream::from(input.into_token_stream())
}
但是,我在处理这些项目时遇到了问题。例如,block
是 Vec<Stmt>
类型,现在一条语句由片段 (Local
, Item
Expr
, Semi
) 和当尝试处理这些作品时,我迷路了。我觉得我遗漏了一些有关如何处理这些片段的信息,但是查看提供的示例 trace-vars
并没有帮助,而且很多在线资源都已过时。
我还尝试了一种非常愚蠢的方法,即使用引号创建 ItemFn
,对其进行解析,并从中获取 Stmt
,但随后由于 [=24 而出现另一个错误=] 实际上来自两个不同的板条箱,proc_macro
和 proc_macro2
:
#[proc_macro_attribute]
pub fn hello(attr: TokenStream, stream: TokenStream) -> TokenStream {
let input = parse_macro_input!(stream as ItemFn);
let res = quote! {fn () { let x = 1; } };
let res = parse_macro_input!(res as ItemFn);
let block = input.block;
block.stmts.insert(0, res.block.stmts[0].clone());
TokenStream::from(input.into_token_stream())
}
error[E0308]: mismatched types
--> bsub-procedural/src/lib.rs:13:15
|
13 | let res = parse_macro_input!(res as ItemFn);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `proc_macro::TokenStream`, found struct `proc_macro2::TokenStream`
运行 在
[dependencies]
syn = { version = "1.0.67", features=["full", "fold", "printing"] }
quote = "1.0.9"
在某些情况下,“愚蠢的方法”实际上是一个非常有效的解决方案。要解析 proc_macro2::TokenStream
,请使用 syn::parse2
而不是宏。
解析似乎是浪费了,因为结果会立即再次转换为令牌流。但对于小事来说,这很好。另一种方法是将函数的所有部分单独转换为标记,例如:
let ItemFn { attrs, vis, sig, block } = input;
let stmts = &block.stmts;
quote! {
#(#attrs)* #vis #sig {
let x = 1; // <- your extra code
#(#stmts)*
}
}
我正在尝试构建一个简单的属性,它将在函数的开头注入一个 let 绑定,因此结果将是:
#[foo]
fn bar(){
// start injected code
let x = 0;
// end injected code
...
}
我已经做到了:
use proc_macro::TokenStream;
use quote::{quote, ToTokens};
use syn::{parse_macro_input, ItemFn, Item, Stmt};
#[proc_macro_attribute]
pub fn hello(attr: TokenStream, stream: TokenStream) -> TokenStream {
let input = parse_macro_input!(stream as ItemFn);
let block = input.block;
let res = quote! {let x = 1;};
// doesn't work, can't convert TokenStream into Stmt
block.stmts.insert(0, res.into());
TokenStream::from(input.into_token_stream())
}
但是,我在处理这些项目时遇到了问题。例如,block
是 Vec<Stmt>
类型,现在一条语句由片段 (Local
, Item
Expr
, Semi
) 和当尝试处理这些作品时,我迷路了。我觉得我遗漏了一些有关如何处理这些片段的信息,但是查看提供的示例 trace-vars
并没有帮助,而且很多在线资源都已过时。
我还尝试了一种非常愚蠢的方法,即使用引号创建 ItemFn
,对其进行解析,并从中获取 Stmt
,但随后由于 [=24 而出现另一个错误=] 实际上来自两个不同的板条箱,proc_macro
和 proc_macro2
:
#[proc_macro_attribute]
pub fn hello(attr: TokenStream, stream: TokenStream) -> TokenStream {
let input = parse_macro_input!(stream as ItemFn);
let res = quote! {fn () { let x = 1; } };
let res = parse_macro_input!(res as ItemFn);
let block = input.block;
block.stmts.insert(0, res.block.stmts[0].clone());
TokenStream::from(input.into_token_stream())
}
error[E0308]: mismatched types
--> bsub-procedural/src/lib.rs:13:15
|
13 | let res = parse_macro_input!(res as ItemFn);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `proc_macro::TokenStream`, found struct `proc_macro2::TokenStream`
运行 在
[dependencies]
syn = { version = "1.0.67", features=["full", "fold", "printing"] }
quote = "1.0.9"
在某些情况下,“愚蠢的方法”实际上是一个非常有效的解决方案。要解析 proc_macro2::TokenStream
,请使用 syn::parse2
而不是宏。
解析似乎是浪费了,因为结果会立即再次转换为令牌流。但对于小事来说,这很好。另一种方法是将函数的所有部分单独转换为标记,例如:
let ItemFn { attrs, vis, sig, block } = input;
let stmts = &block.stmts;
quote! {
#(#attrs)* #vis #sig {
let x = 1; // <- your extra code
#(#stmts)*
}
}