前边的一个随笔里边就写了使用Clang来进行源源变化的一个case,其实还有很多有趣的可以进行的操作。比如,我们可以用添加宏的方式,实现对函数体的修改。
主要代码如下:
//------------------------------------------------------------------------------ // Tooling sample. Demonstrates: // // * How to write a simple source tool using libTooling. // * How to use RecursiveASTVisitor to find interesting AST nodes. // * How to use the Rewriter API to rewrite the source code. // // Eli Bendersky (eliben@gmail.com) // This code is in the public domain //------------------------------------------------------------------------------ #include <sstream> #include <string> #include "clang/AST/AST.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/Frontend/ASTConsumers.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendActions.h" #include "clang/Rewrite/Core/Rewriter.h" #include "clang/Tooling/CommonOptionsParser.h" #include "clang/Tooling/Tooling.h" #include "llvm/Support/raw_ostream.h" using namespace clang; using namespace clang::driver; using namespace clang::tooling; static llvm::cl::OptionCategory ToolingSampleCategory("Tooling Sample"); // By implementing RecursiveASTVisitor, we can specify which AST nodes // we're interested in by overriding relevant methods. class MyASTVisitor : public RecursiveASTVisitor<MyASTVisitor> { public: MyASTVisitor(Rewriter &R) : TheRewriter(R) {} bool VisitFunctionDecl(FunctionDecl *f) { // Only function definitions (with bodies), not declarations. if (f->hasBody()) { Stmt *FuncBody = f->getBody(); SourceLocation slStart = FuncBody->getBeginLoc(); SourceLocation slEnd = FuncBody->getEndLoc(); std::stringstream fbBefore ; fbBefore <<"; #if 0 "; TheRewriter.InsertText(slStart, fbBefore.str(), true, true); std::stringstream fbEnd; fbEnd <<"} #endif "; TheRewriter.ReplaceText(slEnd, fbEnd.str()); } return true; } private: Rewriter &TheRewriter; }; // Implementation of the ASTConsumer interface for reading an AST produced // by the Clang parser. class MyASTConsumer : public ASTConsumer { public: MyASTConsumer(Rewriter &R) : Visitor(R) {} // Override the method that gets called for each parsed top-level // declaration. bool HandleTopLevelDecl(DeclGroupRef DR) override { for (DeclGroupRef::iterator b = DR.begin(), e = DR.end(); b != e; ++b) { // Traverse the declaration using our AST visitor. Visitor.TraverseDecl(*b); (*b)->dump(); } return true; } private: MyASTVisitor Visitor; }; // For each source file provided to the tool, a new FrontendAction is created. class MyFrontendAction : public ASTFrontendAction { public: MyFrontendAction() {} void EndSourceFileAction() override { SourceManager &SM = TheRewriter.getSourceMgr(); llvm::errs() << "** EndSourceFileAction for: " << SM.getFileEntryForID(SM.getMainFileID())->getName() << " "; // Now emit the rewritten buffer. TheRewriter.getEditBuffer(SM.getMainFileID()).write(llvm::outs()); } std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, StringRef file) override { llvm::errs() << "** Creating AST consumer for: " << file << " "; TheRewriter.setSourceMgr(CI.getSourceManager(), CI.getLangOpts()); return std::make_unique<MyASTConsumer>(TheRewriter); } private: Rewriter TheRewriter; }; int main(int argc, const char **argv) { llvm::Expected<CommonOptionsParser> op=CommonOptionsParser::create(argc, argv, ToolingSampleCategory); ClangTool Tool(op.get().getCompilations(), op.get().getSourcePathList()); // ClangTool::run accepts a FrontendActionFactory, which is then used to // create new objects implementing the FrontendAction interface. Here we use // the helper newFrontendActionFactory to create a default factory that will // return a new MyFrontendAction object every time. // To further customize this, we could create our own factory class. return Tool.run(newFrontendActionFactory<MyFrontendAction>().get()); }
我在这里使用的case叫test.cpp,内容如下:
void foo(int* a, int *b) { if (a[0] > 1) { b[0] = 2; } } int main(){ return 0; }
结果截图如下:
或许后边会考虑做一个直接使用Clang的AST接口来实现一个函数的新增?