【问题标题】:Procedural attribute macro to inject code at the beginning of a function在函数开头注入代码的过程属性宏
【发布时间】:2023-04-07 20:16:01
【问题描述】:

我正在尝试构建一个简单的属性,该属性将在函数的开头注入一个 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())
}

但是,我在处理这些项目时遇到了问题。例如,blockVec<Stmt> 类型,现在声明是由片段组成的(LocalItemExprSemi),当尝试处理这些片段时,我只是迷路了.我觉得我遗漏了一些关于如何处理这些部分的内容,但是查看提供的示例 trace-vars 并没有帮助,而且很多在线资源都已过时。

我还尝试了一种非常愚蠢的方法,即使用引号创建ItemFn,对其进行解析并从中获取Stmt,但由于TokenStream 实际上来自两个不同的板条箱,我又遇到了另一个错误, proc_macroproc_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"

【问题讨论】:

    标签: rust rust-macros rust-proc-macros


    【解决方案1】:

    在某些情况下,“愚蠢的方法”实际上是一种非常有效的解决方案。要解析 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)*
        }
    }
    

    【讨论】:

    • 感谢您的回答,这种愚蠢的方法适用于syn::parse2!关于另一个显然更好的解决方案,我得到的错误是the trait 'quote::to_tokens::ToTokens' is not implemented for 'std::vec::Vec&lt;syn::attr::Attribute&gt;'。我已经导入了use quote::ToTokens,并且我检查了Attribute 是否实现了它,它确实实现了。也许是vec 部分没有?我已经为synquote 添加了我的版本,以防这可能是来源,它们都是最新的
    • @Dominus woops,这就是我没有测试我的代码的结果。 #attrs 应该是 #(#attrs)*。我确定了答案。现在应该可以工作了。
    • 感谢您的回答!顺便说一句,如果你有任何好的 proc 宏学习资源,我很乐意阅读它们!
    猜你喜欢
    • 2019-06-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-28
    • 1970-01-01
    • 2013-11-14
    相关资源
    最近更新 更多