使用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 Exprs, as by using visit_expr_mut()