Rust代码分析与模式匹配库ast-grep-language的使用:高效解析和搜索抽象语法树(AST)的工具

Rust代码分析与模式匹配库ast-grep-language的使用:高效解析和搜索抽象语法树(AST)的工具

安装

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

cargo add ast-grep-language

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

ast-grep-language = "0.39.2"

示例使用

下面是一个使用ast-grep-language进行Rust代码分析的完整示例:

use ast_grep_language::{Language, RustLanguage};
use ast_grep_core::{AstGrep, NodeMatch, StrDoc};

fn main() {
    // 1. 创建Rust语言实例
    let language = RustLanguage::default();
    
    // 2. 解析源代码
    let source = r#"
        fn main() {
            println!("Hello, world!");
            let x = 42;
            let y = x + 1;
        }
    "#;
    
    let grep = AstGrep::new(source, language);
    
    // 3. 搜索特定的语法模式
    // 查找所有变量声明
    let pattern = "let $VAR = $INIT;";
    let matches = grep.root().find_all(pattern);
    
    println!("找到 {} 个变量声明:", matches.count());
    for node in matches {
        println!("变量: {}", node.get_match("$VAR").unwrap().text());
        println!("初始值: {}", node.get_match("$INIT").unwrap().text());
    }
    
    // 4. 查找函数调用
    let fn_call_pattern = "$FUNC!($ARGS)";
    let fn_matches = grep.root().find_all(fn_call_pattern);
    
    println!("\n找到 {} 个函数调用:", fn_matches.count());
    for node in fn_matches {
        println!("函数名: {}", node.get_match("$FUNC").unwrap().text());
        if let Some(args) = node.get_match("$ARGS") {
            println!("参数: {}", args.text());
        }
    }
    
    // 5. 查找特定的表达式模式
    let expr_pattern = "$LHS + $RHS";
    let expr_matches = grep.root().find_all(expr_pattern);
    
    println!("\n找到 {} 个加法表达式:", expr_matches.count());
    for node in expr_matches {
        println!("左值: {}", node.get_match("$LHS").unwrap().text());
        println!("右值: {}", node.get_match("$RHS").unwrap().text());
    }
}

完整示例

下面是一个更完整的示例,展示了如何使用ast-grep-language进行更复杂的代码分析和转换:

use ast_grep_language::{Language, RustLanguage};
use ast_grep_core::{AstGrep, NodeMatch, StrDoc, Matcher, Pattern};

fn analyze_code() {
    let rust = RustLanguage::default();
    let source = r#"
        mod utils {
            pub fn helper() -> i32 {
                42
            }
        }
        
        fn main() {
            let result = utils::helper();
            println!("结果是: {}", result);
            
            let items = vec![1, 2, 3];
            let sum: i32 = items.iter().sum();
            
            match sum {
                6 => println!("正确!"),
                _ => println!("错误!"),
            }
        }
    "#;
    
    let grep = AstGrep::new(source, rust);
    
    // 查找模块定义
    let mod_pattern = "mod $NAME { $$$ }";
    let mod_matches = grep.root().find_all(mod_pattern);
    println!("\n模块分析:");
    for node in mod_matches {
        println!("模块名: {}", node.get_match("$NAME").unwrap().text());
    }
    
    // 查找函数调用链
    let method_chain = "$OBJ.$METHOD()";
    let chain_matches = grep.root().find_all(method_chain);
    println!("\n方法调用链分析:");
    for node in chain_matches {
        println!("对象: {}", node.get_match("$OBJ").unwrap().text());
        println!("方法: {}", node.get_match("$METHOD").unwrap().text());
    }
    
    // 查找match表达式
    let match_pattern = "match $EXPR { $$$ }";
    let match_matches = grep.root().find_all(match_pattern);
    println!("\nMatch表达式分析:");
    for node in match_matches {
        println!("匹配表达式: {}", node.get_match("$EXPR").unwrap().text());
    }
    
    // 代码转换示例 - 将let改为const
    let let_pattern = "let $VAR = $INIT;";
    let const_replacement = "const $VAR = $INIT;";
    let transformed = grep.root().replace_pattern(let_pattern, const_replacement);
    println!("\n转换后的代码:");
    println!("{}", transformed);
}

fn main() {
    analyze_code();
}

特性

  1. 高效AST解析:基于Rust的tree-sitter实现快速解析
  2. 模式匹配:使用类似CSS选择器的语法匹配代码模式
  3. 精确搜索:基于语法结构而非文本的搜索
  4. 代码转换:支持基于匹配结果的代码修改和重构
  5. 多语言支持:虽然这里展示的是Rust,但支持多种编程语言

所有者

该项目由Herrington Darkholme维护。


1 回复

Rust代码分析与模式匹配库ast-grep-language的使用指南

简介

ast-grep-language是一个用于高效解析和搜索抽象语法树(AST)的Rust库,它专门为代码分析和模式匹配而设计。这个库提供了强大的工具来解析源代码,并在AST级别进行精确的模式匹配,非常适合构建代码分析工具、重构工具或代码搜索工具。

主要特性

  • 支持多种编程语言的语法解析
  • 高效的AST模式匹配
  • 可扩展的规则系统
  • 精确的源代码定位
  • 支持复杂的查询条件

安装方法

在Cargo.toml中添加依赖:

[dependencies]
ast-grep-language = "0.10"

基本使用方法

1. 解析源代码

use ast_grep_language::Language;
use ast_grep_language::SupportLang;

let lang = SupportLang::Rust.to_lang();
let source = "fn main() { println!(\"Hello\"); }";
let ast = lang.ast_grep(source);

2. 简单模式匹配

use ast_grep_language::{Pattern, Matcher};

let pattern = Pattern::str("println!($ARG)", SupportLang::Rust);
let matches = pattern.find_all(source).collect::<Vec<_>>();

for m in matches {
    println!("Found match at {:?}", m.range());
    println!("Argument: {}", m.get_match("ARG").unwrap().text());
}

3. 复杂模式匹配

let pattern = Pattern::str(r#"
    fn $FUNC($PARAMS) -> $RET {
        $BODY
    }
"#, SupportLang::Rust);

let source = r#"
fn add(a: i32, b: i32) -> i32 {
    a + b
}
"#;

if let Some(m) = pattern.find(source) {
    println!("Function name: {}", m.get_match("FUNC").unwrap().text());
    println!("Parameters: {}", m.get_match("PARAMS").unwrap().text());
    println!("Return type: {}", m.get_match("RET").unwrap().text());
    println!("Body: {}", m.get_match("BODY").unwrap().text());
}

高级用法

1. 使用规则约束匹配

use ast_grep_language::{Pattern, Rule};

let rule = Rule::matches(
    "println!($ARG)",
    r#"{
        "constraints": {
            "ARG": { "regex": "\"Hello\\w*\"" }
        }
    }"#,
    SupportLang::Rust,
).unwrap();

let source = r#"
    println!("Hello");
    println!("HelloWorld");
    println!("Hi");
"#;

let matches = rule.find_all(source).collect::<Vec<_>>();
assert_eq!(matches.len(), 2); // 匹配"Hello"和"HelloWorld"

2. 转换代码

use ast_grep_language::{Pattern, Rewriter};

let pattern = Pattern::str("$A + $B", SupportLang::Rust);
let rewriter = Rewriter::str("$B + $A", SupportLang::Rust);

let source = "let sum = a + b;";
let transformed = pattern.replace(source, &rewriter);
assert_eq!(transformed, "let sum = b + a;");

3. 跨文件分析

use ast_grep_language::{Language, SupportLang};
use std::path::Path;

let lang = SupportLang::Rust.to_lang();
let sg = lang.ast_grep_from_files(["src/**/*.rs"]).unwrap();

// 查找所有名为"new"的函数
let pattern = Pattern::str("fn new($_) -> $_ { $_ }", SupportLang::Rust);
for matched in sg.find(pattern) {
    println!("Found constructor at {}", matched.file_path().display());
}

实际应用示例

1. 查找未使用的变量

let rule = r#"{
    "pattern": "let $VAR = $INIT;",
    "constraints": {
        "VAR": { "not": { "followed_by": "$VAR" } }
    }
}"#;

let rule = Rule::parse(rule, SupportLang::Rust).unwrap();
let source = r#"
    let x = 1;
    println!("{}", x);
    let y = 2;  // 这个变量未被使用
"#;

let unused = rule.find(source).unwrap();
assert_eq!(unused.get_match("VAR").unwrap().text(), "y");

2. 重构代码风格

let pattern = Pattern::str("if ($COND) { $BODY }", SupportLang::Rust);
let rewriter = Rewriter::str("if $COND { $BODY }", SupportLang::Rust);

let source = "if (x > 0) { println!(\"Positive\"); }";
let transformed = pattern.replace(source, &rewriter);
assert_eq!(transformed, "if x > 0 { println!(\"Positive\"); }");

性能提示

  1. 对于大型代码库,考虑并行处理多个文件
  2. 复用Language和Pattern实例以减少解析开销
  3. 使用更具体的模式来减少匹配范围
  4. 对于复杂的分析任务,考虑将AST缓存到磁盘

ast-grep-language为Rust开发者提供了强大的代码分析和操作能力,可以广泛应用于代码质量检查、自动化重构、代码搜索等场景。


完整示例demo

下面是一个完整的示例,展示如何使用ast-grep-language进行代码分析和重构:

use ast_grep_language::{Language, SupportLang, Pattern, Rule, Rewriter};
use std::path::Path;

fn main() {
    // 1. 解析源代码
    let lang = SupportLang::Rust.to_lang();
    let source = r#"
        fn main() {
            println!("Hello");
            let x = 1 + 2;
            let y = x * 3;
            println!("{}", y);
        }
    "#;
    
    let ast = lang.ast_grep(source);
    
    // 2. 查找所有println!宏调用
    println!("=== 查找println!宏调用 ===");
    let pattern = Pattern::str("println!($ARG)", SupportLang::Rust);
    for m in pattern.find_all(source) {
        println!("Found println! at {:?}", m.range());
        println!("Argument: {}", m.get_match("ARG").unwrap().text());
    }
    
    // 3. 查找未使用的变量
    println!("\n=== 查找未使用的变量 ===");
    let rule = Rule::parse(r#"{
        "pattern": "let $VAR = $INIT;",
        "constraints": {
            "VAR": { "not": { "followed_by": "$VAR" } }
        }
    }"#, SupportLang::Rust).unwrap();
    
    if let Some(m) = rule.find(source) {
        println!("Found unused variable: {}", m.get_match("VAR").unwrap().text());
    }
    
    // 4. 重构代码 - 交换加法操作数
    println!("\n=== 重构代码 ===");
    let pattern = Pattern::str("$A + $B", SupportLang::Rust);
    let rewriter = Rewriter::str("$B + $A", SupportLang::Rust);
    let transformed = pattern.replace(source, &rewriter);
    println!("Original code:\n{}", source);
    println!("Transformed code:\n{}", transformed);
    
    // 5. 跨文件分析
    println!("\n=== 跨文件分析 ===");
    let sg = lang.ast_grep_from_files(["src/**/*.rs"]).unwrap();
    let pattern = Pattern::str("fn $FUNC($_) { $_ }", SupportLang::Rust);
    println!("Found functions in project:");
    for matched in sg.find(pattern) {
        println!("- {} at {}", 
            matched.get_match("FUNC").unwrap().text(),
            matched.file_path().display());
    }
}

这个完整示例展示了:

  1. 源代码解析
  2. 简单模式匹配查找println!宏
  3. 使用规则查找未使用变量
  4. 代码重构示例
  5. 跨文件分析查找所有函数

要运行此示例,请确保:

  1. 在Cargo.toml中添加依赖
  2. 创建一个src目录并添加一些Rust源文件用于跨文件分析
  3. 将示例代码放在项目的主文件中运行
回到顶部