Clang通过两种方式访问AST(抽象语法树)
类结构图
调用栈图
初始化CompilerInstance之后,调用其成员函数ExcutionAction, ExcutionAction会间接依次调用FrontendAction的6个成员函数(直接调用的是FrontendAction的三个public 接口,BeginSourceFile,Execute,EndSourceFile),而FrontendAction的ExecuteAction会最终调用语法分析函数ParseAST(未强制要求ParseAST放入ExcuteAction,但ASTFrontendAction如此)。 ParseAST在分析过程中,又会调用ASTConsumer的多个函数(用得最多是HandleTopLevelDecl和 HandleTranslationUnit)。FrontendAction是文件级别的动作,ASTConsumer则与一个Translation Unit内部处理过程相关。RecursiveASTVisitor是针对AST node的遍历,一般需要ASTConsumer中呈现的AST node(如TranslationUnitDecl)作为参数。
以深度优先的方式遍历整个 Clang AST 并访问每个节点的一个类。其执行三种不同的操作:
这些操作由三组类方法来完成,分别是:
这三组方法具有下列层次关系:Traverse* > WalkUpFrom* > Visit*。某一层次的方法可以调用相同层次的另一个方法以及较低层次的方法,但不能调用层次比它高的方法。
ifStmt(hasCondition(expr().bind("C")), hasThen(stmt().bind("T")), hasElse(stmt().bind("E")))在 Clang 中,AST Matcher 被分为了三类:
每一个匹配表达式都以 Node Matchers 开始,然后使用 Narrowing 或 Traversal Matcher 进一步完善。所有遍历匹配器都将 Node Matcher 作为其参数。此外,每个 Node Matcher 都隐含了一个 allOf Matcher。
#include "clang/AST/ASTConsumer.h"#include "clang/AST/RecursiveASTVisitor.h"#include "clang/Frontend/CompilerInstance.h"#include "clang/Frontend/FrontendAction.h"#include "clang/CodeGen/ObjectFilePCHContainerOperations.h"#include "clang/Driver/Options.h"#include "clang/Rewrite/Frontend/FixItRewriter.h"#include "clang/Rewrite/Frontend/FrontendActions.h"#include "clang/StaticAnalyzer/Frontend/FrontendActions.h"#include "clang/Tooling/CommonOptionsParser.h"#include "clang/Tooling/Syntax/BuildTree.h"#include "clang/Tooling/Syntax/Tokens.h"#include "clang/Tooling/Syntax/Tree.h"#include "clang/Tooling/Tooling.h"#include "clang/ASTMatchers/ASTMatchFinder.h"#include "clang/ASTMatchers/ASTMatchers.h"#include "llvm/ADT/STLExtras.h"#include "llvm/Option/OptTable.h"#include "llvm/Support/Path.h"#include "llvm/Support/Signals.h"#include "llvm/Support/TargetSelect.h"using namespace clang;using namespace clang::ast_matchers;using namespace clang::driver;using namespace clang::tooling;using namespace llvm;static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);static cl::extrahelp MoreHelp( " For example, to run rewriter
" "
");static cl::OptionCategory ClangCheckCategory("rewriter options");static const opt::OptTable &Options = getDriverOptTable();static cl::opt VISITORDump("visitor", cl::desc(Options.getOptionHelpText(options::OPT_ast_dump)), cl::cat(ClangCheckCategory));// Visitor 遍历模式class ReWriteClassVisitor : public RecursiveASTVisitor {public: explicit ReWriteClassVisitor(ASTContext *context, SourceManager *sm, Rewriter *rw) : context_(context), sm_(sm), rewriter_(rw) {} bool VisitStmt(Stmt *s) { // 当前文件,排除包含文件 if (sm_->isInMainFile(s->getBeginLoc()) && isa(s)) { IfStmt *IfStatement = cast(s); Stmt *Then = IfStatement->getThen(); rewriter_->InsertText(Then->getBeginLoc(), "// visitor, the 'if' part
", true, true); Stmt *Else = IfStatement->getElse(); if (Else) { rewriter_->InsertText(Else->getBeginLoc(), "// visitor, the 'else' part
", true, true); } } return true; }private: ASTContext *context_; SourceManager *sm_; std::string caller_; Rewriter *rewriter_;};// Matcher 匹配模式class IfStmtHandler : public MatchFinder::MatchCallback {public: IfStmtHandler(Rewriter &Rewrite) : Rewrite(Rewrite) {} virtual void run(const MatchFinder::MatchResult &Result) { if (const IfStmt *IfS = Result.Nodes.getNodeAs("ifStmt")) { const Stmt *Then = IfS->getThen(); Rewrite.InsertText(Then->getBeginLoc(), "// matcher, the 'if' part
", true, true); if (const Stmt *Else = IfS->getElse()) { Rewrite.InsertText(Else->getBeginLoc(), "// matcher the 'else' part
", true, true); } } }private: Rewriter &Rewrite;};class ReWriteClassConsumer : public clang::ASTConsumer {public: explicit ReWriteClassConsumer(ASTContext *context, SourceManager *sm, Rewriter *rw) : visitor_(context, sm, rw), sm_(sm), rewriter_(rw), handlerif_(*rw) {} ~ReWriteClassConsumer() { llvm::outs() << "I have finished rewrite." << "
"; } virtual void Initialize(ASTContext &Context) override { context_ = &Context; matcher_.addMatcher(ifStmt().bind("ifStmt"), &handlerif_); } virtual void HandleTranslationUnit(clang::ASTContext &context) override { if (VISITORDump) { // 使用遍历模式 visitor_.TraverseDecl(context.getTranslationUnitDecl()); } else { // 使用匹配模式 matcher_.matchAST(context); } }private: ReWriteClassVisitor visitor_; ASTContext *context_; SourceManager *sm_; Rewriter *rewriter_; IfStmtHandler handlerif_; MatchFinder matcher_;};class ReWriteClassAction : public clang::ASTFrontendAction {public: void EndSourceFileAction() override { //rewriter_.getEditBuffer(rewriter_.getSourceMgr().getMainFileID()) .write(llvm::outs()); rewriter_.overwriteChangedFiles(); } virtual std::unique_ptr CreateASTConsumer(clang::CompilerInstance &compiler, llvm::StringRef in_file) { // 屏蔽错误信息输出 compiler.getDiagnostics().setClient(new IgnoringDiagConsumer()); // 相当于-w rewriter_.setSourceMgr(compiler.getSourceManager(), compiler.getLangOpts()); return std::make_unique( &compiler.getASTContext(), &compiler.getSourceManager(), &rewriter_); }private: Rewriter rewriter_; };//命令行参数: ast.cpp -- 屏蔽编译数据库找不到int main(int argc, const char **argv) { llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); // Initialize targets for clang module support. llvm::InitializeAllTargets(); llvm::InitializeAllTargetMCs(); llvm::InitializeAllAsmPrinters(); llvm::InitializeAllAsmParsers(); auto ExpectedParser = CommonOptionsParser::create(argc, argv, ClangCheckCategory); if (!ExpectedParser) { llvm::errs() << ExpectedParser.takeError(); return 1; } CommonOptionsParser &OptionsParser = ExpectedParser.get(); ClangTool Tool(OptionsParser.getCompilations(), OptionsParser.getSourcePathList()); std::unique_ptr FrontendFactory; FrontendFactory = newFrontendActionFactory(); return Tool.run(FrontendFactory.get());} 文件:ast.cpp
//for linux,__cdecl// g++ -g test_objetc_member_function_linux.cpp -std=c++11 -I../src -o test_objetc_member_function_linux#include#include "stub.h"using namespace std;templatevoid swap(T &a, T &b){ T temp = a; a = b; b = temp;}class A{ int i;public: int foo(int a){ if(a > 1) cout<<"I am A_foo:"<< a < 1:"<< a < 执行:rewriter -visitor ast.cpp --
结果:
再执行:rewriter ast.cpp --
结果:
| 留言与评论(共有 0 条评论) “” |