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!
- 手动写出结构和枚举
- 使用缩写,以及
- 使用引号来避免这种情况
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
,并用onAstBuilder
将其包装起来。总计:
#![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
}
}
}