You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何访问序列化的Clang抽象语法树(AST)?

基于Clang实现AST遍历工具的完整方案

我最近成功搭建了一套基于Clang的AST遍历工具,核心是通过ASTFrontendAction、ASTConsumer和RecursiveASTVisitor来遍历源文件的所有AST节点,还通过自定义ToolAction实现了每次AST处理前后的自定义逻辑,整个流程跑起来很顺畅,分享下具体实现:

1. 实现RecursiveASTVisitor遍历AST节点

首先定义一个继承自RecursiveASTVisitor的访问器类,重写你需要处理的节点类型对应的VisitXXX方法,比如处理所有声明和语句节点:

#include "clang/AST/RecursiveASTVisitor.h"

class MyASTVisitor : public RecursiveASTVisitor<MyASTVisitor> {
public:
    // 处理所有声明节点(变量、函数、类等)
    bool VisitDecl(Decl *decl) {
        // 这里可以添加你的自定义处理逻辑,比如打印节点信息
        decl->dump();
        return true; // 返回true继续遍历子节点
    }

    // 处理所有语句节点(if、for、赋值等)
    bool VisitStmt(Stmt *stmt) {
        // 自定义语句处理逻辑
        stmt->dump();
        return true;
    }
};

2. 创建ASTConsumer关联访问器

ASTConsumer是Clang用来处理AST的核心类,我们需要在它的HandleTranslationUnit方法中启动刚才定义的访问器:

#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"

class MyASTConsumer : public ASTConsumer {
public:
    void HandleTranslationUnit(ASTContext &context) override {
        MyASTVisitor visitor;
        // 从翻译单元根节点开始遍历整个AST
        visitor.TraverseDecl(context.getTranslationUnitDecl());
    }
};

3. 实现ASTFrontendAction提供Consumer

ASTFrontendAction负责创建我们的ASTConsumer实例,是连接Clang前端和自定义AST处理逻辑的桥梁:

#include "clang/Frontend/ASTFrontendAction.h"
#include "clang/Frontend/CompilerInstance.h"

class MyFrontendAction : public ASTFrontendAction {
public:
    std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &compiler, llvm::StringRef inFile) override {
        // 返回我们自定义的ASTConsumer
        return std::make_unique<MyASTConsumer>();
    }
};

4. 自定义ToolAction实现前后处理逻辑

如果需要在每次处理源文件的AST前后执行自定义逻辑(比如日志记录、资源清理),可以重写ToolAction的runInvocation方法:

#include "clang/Tooling/Tooling.h"
#include "clang/Frontend/CompilerInvocation.h"
#include "clang/Frontend/CompilerInstance.h"

class MyToolAction : public clang::tooling::ToolAction {
public:
    bool runInvocation(std::shared_ptr<clang::CompilerInvocation> invocation,
                       clang::FileManager *files,
                       std::shared_ptr<clang::PCHContainerOperations> pchOps,
                       clang::DiagnosticConsumer *diagConsumer) override {
        // 处理前的自定义逻辑:比如打印当前处理的文件名
        const auto& inputFile = invocation->getFrontendOpts().Inputs[0].getFile();
        llvm::errs() << "* Starting processing file: " << inputFile << "\n";

        // 初始化编译器实例并运行我们的FrontendAction
        clang::CompilerInstance compiler;
        compiler.setInvocation(std::move(invocation));
        compiler.setFileManager(files);
        compiler.setPCHContainerOperations(std::move(pchOps));
        compiler.createDiagnostics(diagConsumer, false);

        std::unique_ptr<clang::FrontendAction> action = std::make_unique<MyFrontendAction>();
        bool success = compiler.ExecuteAction(*action);

        // 处理后的自定义逻辑:比如打印处理结果状态
        llvm::errs() << "* Finished processing file: " << inputFile 
                     << ", success: " << (success ? "✅" : "❌") << "\n\n";

        return success;
    }
};

5. 主函数中启动ClangTool

最后在主函数中配置ClangTool,并传入我们自定义的ToolAction:

#include "clang/Tooling/CommonOptionsParser.h"
#include "llvm/Support/CommandLine.h"

static llvm::cl::OptionCategory MyToolCategory("My AST Traversal Tool Options");

int main(int argc, const char **argv) {
    // 解析命令行参数
    clang::tooling::CommonOptionsParser optionsParser(argc, argv, MyToolCategory);
    clang::tooling::ClangTool tool(optionsParser.getCompilations(), optionsParser.getSourcePathList());

    // 运行自定义ToolAction
    return tool.run(std::make_unique<MyToolAction>().get());
}

整个方案目前运行稳定,自定义的AST处理逻辑和前后置操作都能正常工作,适合用来做代码分析、静态检查这类场景。

内容的提问来源于stack exchange,提问作者JMLoy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 08:07:57