Rust 如何编写将代码注入函数的自定义属性

Rust 如何编写将代码注入函数的自定义属性,rust,Rust,我已经调用了自定义属性: #[plugin_registrar] pub fn registrar(reg: &mut rustc::plugin::Registry) { use syntax::parse::token::intern; use syntax::ext::base; // Register the `#[dummy]` attribute. reg.register_syntax_extension(intern("dummy"), base::

我已经调用了自定义属性:

#[plugin_registrar]
pub fn registrar(reg: &mut rustc::plugin::Registry) {
  use syntax::parse::token::intern;
  use syntax::ext::base;

  // Register the `#[dummy]` attribute.
  reg.register_syntax_extension(intern("dummy"),
  base::ItemDecorator(dummy_expand));
}

// Decorator for `dummy` attribute
pub fn dummy_expand(context: &mut ext::base::ExtCtxt, span: codemap::Span, meta_item: Gc<ast::MetaItem>, item: Gc<ast::Item>, push: |Gc<ast::Item>|) {
  match item.node {
    ast::ItemFn(decl, ref style, ref abi, ref generics, block) => {
      trace!("{}", decl);
      // ...? Add something here.
    }
    _ => {
      context.span_err(span, "dummy is only permissiable on functions");
    }
  }
}
…我已经看到了一些使用
quote\u expr!(…)
这样做,但我真的不理解他们

假设我想将此语句(或者它是表达式?)添加到任何标记为
#[dummy]
的函数的顶部:

println!("dummy");

如何实现这一目标?

这里有两项任务:

  • 正在创建要插入的AST
  • 转换某些函数的AST(例如插入另一个片段)
注:

  • 当我在这个答案中说“item”时,我的具体意思是,例如,
    fn
    struct
    impl
  • 当使用宏执行任何操作时,
    rustc--pretty expanded foo.rs
    是您最好的朋友(在最小的示例中效果最好,例如避免
    #[派生]
    println!
    ,除非您特别尝试调试它们)
AST创建 有3种基本方法可以从头开始创建块:

  • 手动写出结构和枚举
  • 使用缩写,以及
  • 使用引号来避免这种情况
在这种情况下,我们可以使用报价,所以我不会在其他方面浪费时间。
quote
宏采用
ExtCtxt
(“扩展上下文”)和表达式或项等,并创建表示该项的AST值,例如

let x: Gc<ast::Expr> = quote_expr!(cx, 1 + 2);
如果1+2>0{println!(“dummy”)},将创建一个AST reprsenting

这一切都非常不稳定,而且宏是功能门控的。完整的“工作”示例:

这是在

reg.register_syntax_extension(intern("dummy"), base::ItemModifier(dummy_expand));
我们已经有了样板设置,我们只需要编写实现。有两种方法。我们可以添加
println
到函数内容的开头,或者我们可以从
foo()更改内容;条()
println!(“虚拟”);{foo();bar();…}
只需创建两个新表达式

您发现,
ItemFn
可以与

ast::ItemFn(decl, ref style, ref abi, ref generics, block)
其中,
block
是实际内容。我上面提到的第二种方法是最简单的,只是

let new_contents = quote_expr!(cx, 
    println!("dummy");
    $block
);
然后为了保存旧信息,我们将构造一个新的
ItemFn
,并用on
AstBuilder
将其包装起来。总计:

#![feature(quote, plugin_registrar)]
#![crate_type = "dylib"]

// general boilerplate
extern crate syntax;
extern crate rustc;

use syntax::ast;
use syntax::codemap::Span;
use syntax::ext::base::{ExtCtxt, ItemModifier};
// NB. this is important or the method calls don't work
use syntax::ext::build::AstBuilder;
use syntax::parse::token;

use std::gc::Gc;

#[plugin_registrar]
pub fn registrar(reg: &mut rustc::plugin::Registry) {
  // Register the `#[dummy]` attribute.
  reg.register_syntax_extension(token::intern("dummy"),
                                ItemModifier(dummy_expand));
}

fn dummy_expand(cx: &mut ExtCtxt, sp: Span, _: Gc<ast::MetaItem>, 
                item: Gc<ast::Item>) -> Gc<ast::Item> {
    match item.node {
        ast::ItemFn(decl, ref style, ref abi, ref generics, block) => {
            let new_contents = quote_expr!(&mut *cx,
                println!("dummy");
                $block
            );
            let new_item_ = ast::ItemFn(decl, style.clone(), 
                                        abi.clone(), generics.clone(),
                                        // AstBuilder to create block from expr
                                        cx.block_expr(new_contents));
            // copying info from old to new
            cx.item(item.span, item.ident, item.attrs.clone(), new_item_)
        }
        _ => {
            cx.span_err(sp, "dummy is only permissible on functions");
            item
        }
    }
}
#![功能(引用,插件注册商)]
#![板条箱类型=“dylib”]
//通用样板
外部板条箱语法;
外部板条箱生锈;
使用syntax::ast;
使用syntax::codemap::Span;
使用语法::ext::base::{ExtCtxt,ItemModifier};
//注意。这很重要,否则方法调用不起作用
使用语法::ext::build::AstBuilder;
使用syntax::parse::token;
使用std::gc::gc;
#[插件注册器]
发布fn注册(注册:&mut-rustc::plugin::Registry){
//注册“#[dummy]”属性。
注册表语法扩展(令牌::实习生(“虚拟”),
ItemModifier(dummy_expand));
}
fn虚拟扩展(cx:&mut ExtCtxt,sp:Span,uU:Gc,
项目:Gc)->Gc{
match item.node{
ast::ItemFn(decl,ref-style,ref-abi,ref-generics,block)=>{
让new_contents=quote_expr!(&mut*cx,
println!(“虚拟”);
$block
);
让new_item_uu=ast::ItemFn(decl,style.clone(),
abi.clone(),泛型.clone(),
//AstBuilder从expr创建块
cx.块扩展(新内容);
//将信息从旧复制到新
cx.item(item.span、item.ident、item.attrs.clone()、new_item)
}
_ => {
cx.span_err(sp,“仅允许在函数上使用伪函数”);
项目
}
}
}

hm。。。尝试此操作时,创建的新_contents块将丢弃函数的现有内容,结果函数仅包含println!('dummy')语句。有什么建议吗?(@Doug(特别是最后提到的工作)。
reg.register_syntax_extension(intern("dummy"), base::ItemModifier(dummy_expand));
ast::ItemFn(decl, ref style, ref abi, ref generics, block)
let new_contents = quote_expr!(cx, 
    println!("dummy");
    $block
);
#![feature(quote, plugin_registrar)]
#![crate_type = "dylib"]

// general boilerplate
extern crate syntax;
extern crate rustc;

use syntax::ast;
use syntax::codemap::Span;
use syntax::ext::base::{ExtCtxt, ItemModifier};
// NB. this is important or the method calls don't work
use syntax::ext::build::AstBuilder;
use syntax::parse::token;

use std::gc::Gc;

#[plugin_registrar]
pub fn registrar(reg: &mut rustc::plugin::Registry) {
  // Register the `#[dummy]` attribute.
  reg.register_syntax_extension(token::intern("dummy"),
                                ItemModifier(dummy_expand));
}

fn dummy_expand(cx: &mut ExtCtxt, sp: Span, _: Gc<ast::MetaItem>, 
                item: Gc<ast::Item>) -> Gc<ast::Item> {
    match item.node {
        ast::ItemFn(decl, ref style, ref abi, ref generics, block) => {
            let new_contents = quote_expr!(&mut *cx,
                println!("dummy");
                $block
            );
            let new_item_ = ast::ItemFn(decl, style.clone(), 
                                        abi.clone(), generics.clone(),
                                        // AstBuilder to create block from expr
                                        cx.block_expr(new_contents));
            // copying info from old to new
            cx.item(item.span, item.ident, item.attrs.clone(), new_item_)
        }
        _ => {
            cx.span_err(sp, "dummy is only permissible on functions");
            item
        }
    }
}