使用VisitMut时如何插入Expr?
How to insert Expr when using VisitMut?
我目前正在使用 syn 按照示例来创建可以变异的 AST。我知道我可以 mod 验证我正在遍历的节点(如下面我当前的代码所示)但是
我很好奇是否可以在当前节点和下一个节点之间添加一些代码。 syn crate 有这个能力吗?
use syn::visit_mut::{self, VisitMut};
use syn::Expr;
#[derive(Debug)]
struct MyStruct;
impl VisitMut for MyStruct {
fn visit_expr_mut(&mut self, node: &mut Expr) {
if let Expr::MethodCall(expr) = &node.to_owned() {
// I can modify the existing node like so:
*node = parse_quote!("// Hello World");
// How could I add something after this node and before the next?
}
}
}
pub fn create() {
let current_dir = std::env::current_dir().expect("Unable to get current directory");
let rust_file = std::fs::read_to_string(current_dir.join("src").join("lib.rs")).expect("Unable to read rust file");
let ast = syn::parse_file(&rust_file).expect("Unable to create AST from rust file");
MyStruct.visit_file_mut(&mut ast);
}
编辑以显示用例:
我当前正在解析的文件如下所示:
#[macro_use]
extern crate foo;
mod test;
fn init(handle: foo::InitHandle) {
handle.add_class::<Test::test>();
}
假设当我阅读 AST 时,我想为它添加另一个 mod 和另一个句柄,如下所示:
#[macro_use]
extern crate foo;
mod test;
mod store;
fn init(handle: foo::InitHandle) {
handle.add_class::<Test::test>();
handle.add_class::<Store::store>();
}
正如我评论的那样,这在很大程度上取决于您想要 插入 的内容。因为您不能轻易地在 node
之前或之后插入任何内容。
对于您的具体情况,您可以使用 parse_quote!
to produce an ExprBlock
。
*node = parse_quote!(
{
#expr;
handle.add_class::<Store::store>();
}
);
其中输入如下:
fn init(handle: foo::InitHandle) {
handle.add_class::<Test::test>();
}
会产生这个输出:
fn init(handle: foo::InitHandle) {
{
handle.add_class::<Test::test>();
handle.add_class::<Store::store>();
};
}
(注意我重新格式化了输出,为了更漂亮)
或者,您可以覆盖 visit_block_mut()
instead. That way you'd have access to stmts: Vec<Stmt>
, and would be able to insert before and after a Stmt
. The downside is that by doing it that way, you wouldn't be able to easily visit all Expr
s, as by using visit_expr_mut()
。
我目前正在使用 syn 按照示例来创建可以变异的 AST。我知道我可以 mod 验证我正在遍历的节点(如下面我当前的代码所示)但是 我很好奇是否可以在当前节点和下一个节点之间添加一些代码。 syn crate 有这个能力吗?
use syn::visit_mut::{self, VisitMut};
use syn::Expr;
#[derive(Debug)]
struct MyStruct;
impl VisitMut for MyStruct {
fn visit_expr_mut(&mut self, node: &mut Expr) {
if let Expr::MethodCall(expr) = &node.to_owned() {
// I can modify the existing node like so:
*node = parse_quote!("// Hello World");
// How could I add something after this node and before the next?
}
}
}
pub fn create() {
let current_dir = std::env::current_dir().expect("Unable to get current directory");
let rust_file = std::fs::read_to_string(current_dir.join("src").join("lib.rs")).expect("Unable to read rust file");
let ast = syn::parse_file(&rust_file).expect("Unable to create AST from rust file");
MyStruct.visit_file_mut(&mut ast);
}
编辑以显示用例:
我当前正在解析的文件如下所示:
#[macro_use]
extern crate foo;
mod test;
fn init(handle: foo::InitHandle) {
handle.add_class::<Test::test>();
}
假设当我阅读 AST 时,我想为它添加另一个 mod 和另一个句柄,如下所示:
#[macro_use]
extern crate foo;
mod test;
mod store;
fn init(handle: foo::InitHandle) {
handle.add_class::<Test::test>();
handle.add_class::<Store::store>();
}
正如我评论的那样,这在很大程度上取决于您想要 插入 的内容。因为您不能轻易地在 node
之前或之后插入任何内容。
对于您的具体情况,您可以使用 parse_quote!
to produce an ExprBlock
。
*node = parse_quote!(
{
#expr;
handle.add_class::<Store::store>();
}
);
其中输入如下:
fn init(handle: foo::InitHandle) {
handle.add_class::<Test::test>();
}
会产生这个输出:
fn init(handle: foo::InitHandle) {
{
handle.add_class::<Test::test>();
handle.add_class::<Store::store>();
};
}
(注意我重新格式化了输出,为了更漂亮)
或者,您可以覆盖 visit_block_mut()
instead. That way you'd have access to stmts: Vec<Stmt>
, and would be able to insert before and after a Stmt
. The downside is that by doing it that way, you wouldn't be able to easily visit all Expr
s, as by using visit_expr_mut()
。