Rust 过程宏(proc-macro)开发工具包 ra_ap_proc_macro_api 的使用,支持语法树解析与代码生成

Rust 过程宏(proc-macro)开发工具包 ra_ap_proc_macro_api 的使用,支持语法树解析与代码生成

安装

在项目目录中运行以下 Cargo 命令:

cargo add ra_ap_proc_macro_api

或者在 Cargo.toml 中添加以下行:

ra_ap_proc_macro_api = "0.0.300"

基本信息

ra_ap_proc_macro_api 示例

以下是一个使用 ra_ap_proc_macro_api 进行语法树解析和代码生成的完整示例:

use ra_ap_proc_macro_api::{
    ProcMacroKind, RustAnalyzerProcMacro, TokenStream,
};

// 创建一个派生宏示例
#[proc_macro_derive(MyDeriveMacro)]
pub fn my_derive_macro(input: TokenStream) -> TokenStream {
    // 解析输入 TokenStream
    let syntax_tree = syn::parse_macro_input!(input as syn::DeriveInput);
    
    // 获取结构体/枚举名称
    let name = &syntax_tree.ident;
    
    // 生成实现代码
    let expanded = quote::quote! {
        impl #name {
            fn hello_macro() {
                println!("Hello, Macro! My name is {}!", stringify!(#name));
            }
        }
    };
    
    // 返回生成的 TokenStream
    expanded.into()
}

// 注册宏到 rust-analyzer
pub fn rust_analyzer_proc_macros() -> Vec<RustAnalyzerProcMacro> {
    vec![RustAnalyzerProcMacro {
        name: "my_derive_macro".to_string(),
        kind: ProcMacroKind::CustomDerive,
        expander: Box::new(|input| {
            my_derive_macro(input)
                .unwrap_or_else(|err| err.to_compile_error())
                .into()
        }),
    }]
}

功能说明

  1. TokenStream 处理:

    • 可以接收和返回 Rust 代码的 TokenStream 表示
    • 与标准库 proc_macro 兼容
  2. 语法树解析:

    • 使用 syn 库解析输入的 TokenStream
    • 支持解析各种 Rust 语法结构
  3. 代码生成:

    • 使用 quote 库生成新的 Rust 代码
    • 可以创建自定义的派生宏、属性宏和函数式宏
  4. 与 rust-analyzer 集成:

    • 提供专门的结构体与 rust-analyzer 交互
    • 支持在 IDE 中获得更好的宏支持

这个工具包是 rust-analyzer 项目的一部分,专门为开发高质量的过程宏提供支持,特别是在 IDE 环境中。

完整示例代码

以下是一个更完整的示例,展示如何创建一个属性宏并使用 ra_ap_proc_macro_api 进行开发:

use ra_ap_proc_macro_api::{
    ProcMacroKind, RustAnalyzerProcMacro, TokenStream,
};
use syn::{parse_macro_input, ItemFn};
use quote::quote;

// 创建一个属性宏示例
#[proc_macro_attribute]
pub fn log_execution_time(
    _attr: TokenStream,  // 属性参数
    item: TokenStream   // 被标记的函数
) -> TokenStream {
    // 解析输入的函数
    let input_fn = parse_macro_input!(item as ItemFn);
    let fn_name = &input_fn.sig.ident;
    
    // 生成修改后的函数
    let output = quote! {
        #input_fn
        
        // 自动添加的计时功能
        impl #fn_name {
            pub fn timed_execution(&self) {
                use std::time::Instant;
                let start = Instant::now();
                self.#fn_name();
                let duration = start.elapsed();
                println!("{} executed in {:?}", stringify!(#fn_name), duration);
            }
        }
    };
    
    output.into()
}

// 注册所有宏到 rust-analyzer
pub fn rust_analyzer_proc_macros() -> Vec<RustAnalyzerProcMacro> {
    vec![
        RustAnalyzerProcMacro {
            name: "log_execution_time".to_string(),
            kind: ProcMacroKind::Attr,
            expander: Box::new(|attr, item| {
                log_execution_time(attr, item)
                    .unwrap_or_else(|err| err.to_compile_error())
                    .into()
            }),
        },
        // 可以继续添加其他宏...
    ]
}

使用示例

#[log_execution_time]
fn my_function() {
    // 一些耗时操作...
    std::thread::sleep(std::time::Duration::from_secs(1));
}

fn main() {
    let instance = my_function();
    instance.timed_execution();  // 自动打印执行时间
}

这个完整示例展示了:

  1. 如何创建一个属性宏
  2. 如何使用 syn 解析函数定义
  3. 如何使用 quote 生成新代码
  4. 如何注册宏到 rust-analyzer
  5. 实际使用示例

通过 ra_ap_proc_macro_api,开发者可以方便地创建各种类型的过程宏,并与 rust-analyzer 良好集成。


1 回复

Rust 过程宏开发工具包 ra_ap_proc_macro_api 使用指南

简介

ra_ap_proc_macro_api 是 Rust Analyzer 项目提供的一个过程宏开发工具包,它提供了强大的语法树解析和代码生成功能,简化了 Rust 过程宏的开发工作。

这个库特别适合需要精细控制代码生成或进行复杂语法分析的过程宏开发者,它比标准库提供的 proc_macro 更强大且更易用。

主要功能

  1. 语法树解析和遍历
  2. 代码生成和转换
  3. 源代码位置信息保留
  4. 与 Rust Analyzer 共享的语法树表示

安装

在 Cargo.toml 中添加依赖:

[dependencies]
ra_ap_proc_macro_api = "0.3"

基本使用方法

1. 解析 Rust 代码

use ra_ap_proc_macro_api::parse::{parse_expr, parse_item};

fn main() {
    // 解析表达式
    let expr = parse_expr("1 + 2 * 3").unwrap();
    println!("Parsed expression: {:?}", expr);
    
    // 解析项(item)
    let item = parse_item("fn foo() -> u32 { 42 }").unwrap();
    println!("Parsed item: {:?}", item);
}

2. 创建自定义派生宏

use proc_macro::TokenStream;
use ra_ap_proc_macro_api::{TokenTree, Literal, Punct, Spacing};

#[proc_macro_derive(HelloMacro)]
pub fn hello_macro_derive(input: TokenStream) -> TokenStream {
    // 将 TokenStream 转换为 ra_ap_proc_macro_api 的表示
    let tt = TokenTree::from_token_stream(input);
    
    // 处理语法树并生成新代码
    let output = vec![
        TokenTree::Literal(Literal::string("Hello from macro!")),
        TokenTree::Punct(Punct::new(',', Spacing::Alone)),
    ];
    
    // 转换回标准 TokenStream
    TokenTree::to_token_stream(&output)
}

3. 语法树遍历和修改

use ra_ap_proc_macro_api::{visit_mut, SyntaxNode};

// 定义一个访问器来修改语法树
struct MyVisitor;

impl visit_mut::VisitMut for MyVisitor {
    fn visit_expr_mut(&mut self, node: &mut SyntaxNode) {
        // 对表达式节点进行处理
        // ...
        visit_mut::walk_expr_mut(self, node);
    }
}

fn transform_code(input: &str) -> String {
    let mut node = ra_ap_proc_macro_api::parse_expr(input).unwrap();
    let mut visitor = MyVisitor;
    visitor.visit_expr_mut(&mut node);
    node.to_string()
}

高级用法示例

1. 属性宏实现

use ra_ap_proc_macro_api::{parse_item, visit_mut, SyntaxNode};

#[proc_macro_attribute]
pub fn log_call(_attr: TokenStream, item: TokenStream) -> TokenStream {
    let mut item = parse_item(&item.to_string()).unwrap();
    
    // 查找函数定义并修改
    struct FunctionVisitor;
    
    impl visit_mut::VisitMut for FunctionVisitor {
        fn visit_fn_mut(&mut self, node: &mut SyntaxNode) {
            // 在函数开始处添加日志调用
            let log_stmt = parse_stmt("println!(\"Function called\");").unwrap();
            node.prepend_child(log_stmt);
        }
    }
    
    let mut visitor = FunctionVisitor;
    visitor.visit_item_mut(&mut item);
    
    item.to_token_stream()
}

2. 代码生成器

use ra_ap_proc_macro_api::{SyntaxNode, SyntaxKind};

fn generate_struct(name: &str, fields: &[(&str, &str)]) -> SyntaxNode {
    let mut struct_node = SyntaxNode::new(SyntaxKind::STRUCT);
    
    struct_node.set_ident(name);
    
    for (field_name, field_ty) in fields {
        let field = SyntaxNode::new(SyntaxKind::STRUCT_FIELD)
            .with_ident(*field_name)
            .with_type(*field_ty);
        struct_node.push_child(field);
    }
    
    struct_node
}

完整示例代码

下面是一个完整的使用 ra_ap_proc_macro_api 创建自定义派生宏的示例:

use proc_macro::TokenStream;
use ra_ap_proc_macro_api::{TokenTree, Literal, Punct, Spacing};

/// 一个简单的派生宏,为结构体生成默认实现
#[proc_macro_derive(DefaultStruct)]
pub fn default_struct_derive(input: TokenStream) -> TokenStream {
    // 将输入 TokenStream 转换为 ra_ap_proc_macro_api 的 TokenTree
    let tt = TokenTree::from_token_stream(input);
    
    // 获取结构体名称
    let struct_name = if let Some(ident) = tt.get_ident() {
        ident.to_string()
    } else {
        panic!("Expected a struct name");
    };
    
    // 生成默认实现代码
    let output = vec![
        TokenTree::Literal(Literal::string("impl Default for ")),
        TokenTree::Literal(Literal::string(&struct_name)),
        TokenTree::Literal(Literal::string(" { ")),
        TokenTree::Punct(Punct::new('#', Spacing::Alone)),
        TokenTree::Literal(Literal::string("[derive(Default)]")),
        TokenTree::Punct(Punct::new('#', Spacing::Alone)),
        TokenTree::Literal(Literal::string(" fn default() -> Self { Self::default() } }")),
    ];
    
    // 转换回标准 TokenStream
    TokenTree::to_token_stream(&output)
}

注意事项

  1. 这个库主要面向过程宏开发者,需要熟悉 Rust 语法树结构
  2. 与标准库的 proc_macro 相比,它提供了更丰富的 API 但可能有不同的性能特征
  3. 由于是 Rust Analyzer 的一部分,API 可能会随着 Rust Analyzer 的更新而变化

总结

ra_ap_proc_macro_api 为 Rust 过程宏开发提供了强大而灵活的工具,特别适合需要精细控制代码生成或进行复杂语法分析的场景。通过使用这个库,开发者可以更轻松地构建高质量的过程宏。

回到顶部