Rust语法树搜索与代码转换工具ast-grep-config的使用,支持高效AST模式匹配与重构

Rust语法树搜索与代码转换工具ast-grep-config的使用,支持高效AST模式匹配与重构

安装方法

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

cargo add ast-grep-config

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

ast-grep-config = "0.39.2"

示例代码

下面是一个使用ast-grep-config进行AST模式匹配和代码转换的完整示例:

use ast_grep_config::{RuleConfig, SgRootConfig};
use std::path::Path;

fn main() {
    // 1. 创建一个规则配置来匹配所有println!宏调用
    let rule = RuleConfig {
        id: "no-println".to_string(),
        pattern: "println!($ARGS)".to_string(),
        message: "Avoid using println! macro".to_string(),
        severity: "warning".to_string(),
        ..Default::default()
    };

    // 2. 创建根配置并添加规则
    let mut config = SgRootConfig::default();
    config.rules.push(rule);

    // 3. 扫描项目目录
    let project_path = Path::new("./src");
    let matches = config.scan(project_path).unwrap();

    // 4. 处理匹配结果
    for m in matches {
        println!("Found println! at {}:{}", m.file_path, m.line_number);
        // 这里可以添加代码转换逻辑,例如替换为log::info!
    }

    // 5. 代码转换示例
    let transform_rule = RuleConfig {
        id: "replace-println".to_string(),
        pattern: "println!($ARGS)".to_string(),
        fix: "log::info!($ARGS)".to_string(),
        ..Default::default()
    };

    let mut transform_config = SgRootConfig::default();
    transform_config.rules.push(transform_rule);

    // 6. 执行代码转换
    let _ = transform_config.replace(project_path);
}

完整功能示例

use ast_grep_config::{RuleConfig, SgRootConfig};
use std::path::Path;

fn main() {
    // 示例1: 检测所有未处理的Result
    let result_rule = RuleConfig {
        id: "unhandled-result".to_string(),
        pattern: "$VAR.unwrap()".to_string(),
        message: "Avoid using unwrap() directly".to_string(),
        severity: "error".to_string(),
        ..Default::default()
    };

    // 示例2: 检测所有panic!调用
    let panic_rule = RuleConfig {
        id: "no-panic".to_string(),
        pattern: "panic!($ARGS)".to_string(),
        message: "Avoid using panic! macro".to_string(),
        severity: "error".to_string(),
        ..Default::default()
    };

    // 创建配置并添加规则
    let mut config = SgRootConfig::default();
    config.rules.push(result_rule);
    config.rules.push(panic_rule);

    // 扫描项目目录
    let project_path = Path::new("./src");
    let matches = config.scan(project_path).unwrap();

    // 处理检测结果
    for m in matches {
        println!("发现潜在问题: {} 在 {}:{}", 
            m.message, 
            m.file_path, 
            m.line_number
        );
    }

    // 代码转换示例: 将unwrap()转换为expect()
    let transform_rule = RuleConfig {
        id: "replace-unwrap".to_string(),
        pattern: "$VAR.unwrap()".to_string(),
        fix: "$VAR.expect(\"处理错误\")".to_string(),
        ..Default::default()
    };

    let mut transform_config = SgRootConfig::default();
    transform_config.rules.push(transform_rule);

    // 执行代码转换
    let _ = transform_config.replace(project_path);
}

功能说明

  1. AST模式匹配:通过定义规则模式,可以精确匹配代码中的特定语法结构
  2. 代码重构:支持自动替换匹配到的代码片段
  3. 批量处理:可以扫描整个项目目录并批量处理所有匹配项

该工具使用MIT许可证,由Herrington Darkholme维护。


1 回复

Rust语法树搜索与代码转换工具ast-grep-config使用指南

工具介绍

ast-grep-config是一个基于抽象语法树(AST)的Rust代码搜索与转换工具,它允许开发者通过模式匹配的方式高效地查找和重构Rust代码。该工具特别适合大规模代码库的重构、代码质量检查和自动化代码转换。

主要特性

  • 精确的AST模式匹配
  • 支持代码转换和重构
  • 高性能的语法树遍历
  • 可配置的匹配规则
  • 支持自定义规则集

安装方法

cargo install ast-grep-config

基本使用方法

1. 简单搜索

ast-grep-config search 'fn $FUNC($_) { $_ }'

这个命令会搜索所有函数定义。

2. 模式匹配示例

// 匹配所有使用unwrap()的调用
ast-grep-config search '$T.unwrap()'

3. 代码转换示例

创建一个规则配置文件unwrap_to_expect.yml:

rule:
  pattern: '$T.unwrap()'
  rewrite: '$T.expect("TODO: add error message")'

然后运行转换:

ast-grep-config rewrite -c unwrap_to_expect.yml

4. 复杂模式匹配

# match_result.yml
rule:
  pattern: |
    match $EXPR {
      Ok($_) => $GOOD,
      Err($_) => $BAD,
    }
  rewrite: |
    $EXPR.map(|it| $GOOD).map_err(|_| $BAD)

运行转换:

ast-grep-config rewrite -c match_result.yml

高级用法

1. 条件匹配

rule:
  pattern: 'let $V = $EXPR;'
  constraints:
    EXPR:
      pattern: '$T.unwrap()'
  rewrite: 'let $V = $T.expect("TODO");'

2. 作用域限定

rule:
  pattern: '$T.unwrap()'
  constraints:
    T:
      inside:
        kind: method_call
        pattern: '$X.build()'
  rewrite: '$T.expect("failed to build")'

3. 元变量转换

rule:
  pattern: 'println!("{}", $ARG);'
  rewrite: 'println!("{:?}", $ARG);'

实际应用场景

  1. 错误处理改进:将unwrap()批量替换为expect()
  2. API迁移:当库API变更时自动更新调用代码
  3. 代码风格统一:强制执行团队编码规范
  4. 代码质量检查:查找潜在的不良模式
  5. 测试代码生成:基于现有代码自动生成测试用例

配置文件示例

# 完整的配置示例
id: unwrap-to-expect
message: Replace unwrap with expect
severity: warning
rule:
  pattern: '$T.unwrap()'
  rewrite: '$T.expect("TODO: add error message")'

完整示例demo

下面是一个完整的示例,展示如何使用ast-grep-config进行代码转换:

  1. 首先创建一个Rust源文件example.rs:
fn main() {
    let result = some_operation().unwrap(); // 需要替换的unwrap调用
    
    // 另一个需要替换的unwrap
    let value = another_call()
        .and_then(|x| x.process())
        .unwrap();
        
    // 匹配示例中的match表达式
    match some_result() {
        Ok(data) => process_data(data),
        Err(e) => handle_error(e),
    }
}
  1. 创建转换规则文件unwrap_to_expect.yml:
id: replace-unwrap
message: Replace unwrap with expect
severity: warning
rule:
  pattern: '$T.unwrap()'
  rewrite: '$T.expect("TODO: add error message")'
  1. 创建复杂匹配规则文件match_to_map.yml:
id: match-to-map
message: Convert match to map/map_err
severity: info
rule:
  pattern: |
    match $EXPR {
      Ok($_) => $GOOD,
      Err($_) => $BAD,
    }
  rewrite: |
    $EXPR.map(|it| $GOOD).map_err(|_| $BAD)
  1. 运行转换命令:
# 替换unwrap调用
ast-grep-config rewrite -c unwrap_to_expect.yml -i example.rs

# 转换match表达式
ast-grep-config rewrite -c match_to_map.yml -i example.rs

转换后的代码将变为:

fn main() {
    let result = some_operation().expect("TODO: add error message"); // unwrap已替换
    
    let value = another_call()
        .and_then(|x| x.process())
        .expect("TODO: add error message");
        
    // match表达式已转换
    some_result()
        .map(|it| process_data(it))
        .map_err(|_| handle_error(it))
}

ast-grep-config通过强大的AST模式匹配能力,可以显著提高Rust代码重构和维护的效率。

回到顶部