├── Example.cpp ├── Makefile ├── README.md ├── run_example.sh └── test.c /Example.cpp: -------------------------------------------------------------------------------- 1 | #include "clang/Driver/Options.h" 2 | #include "clang/AST/AST.h" 3 | #include "clang/AST/ASTContext.h" 4 | #include "clang/AST/ASTConsumer.h" 5 | #include "clang/AST/RecursiveASTVisitor.h" 6 | #include "clang/Frontend/ASTConsumers.h" 7 | #include "clang/Frontend/FrontendActions.h" 8 | #include "clang/Frontend/CompilerInstance.h" 9 | #include "clang/Tooling/CommonOptionsParser.h" 10 | #include "clang/Tooling/Tooling.h" 11 | #include "clang/Rewrite/Core/Rewriter.h" 12 | 13 | using namespace std; 14 | using namespace clang; 15 | using namespace clang::driver; 16 | using namespace clang::tooling; 17 | using namespace llvm; 18 | 19 | Rewriter rewriter; 20 | int numFunctions = 0; 21 | 22 | 23 | class ExampleVisitor : public RecursiveASTVisitor { 24 | private: 25 | ASTContext *astContext; // used for getting additional AST info 26 | 27 | public: 28 | explicit ExampleVisitor(CompilerInstance *CI) 29 | : astContext(&(CI->getASTContext())) // initialize private members 30 | { 31 | rewriter.setSourceMgr(astContext->getSourceManager(), astContext->getLangOpts()); 32 | } 33 | 34 | virtual bool VisitFunctionDecl(FunctionDecl *func) { 35 | numFunctions++; 36 | string funcName = func->getNameInfo().getName().getAsString(); 37 | if (funcName == "do_math") { 38 | rewriter.ReplaceText(func->getLocation(), funcName.length(), "add5"); 39 | errs() << "** Rewrote function def: " << funcName << "\n"; 40 | } 41 | return true; 42 | } 43 | 44 | virtual bool VisitStmt(Stmt *st) { 45 | if (ReturnStmt *ret = dyn_cast(st)) { 46 | rewriter.ReplaceText(ret->getRetValue()->getLocStart(), 6, "val"); 47 | errs() << "** Rewrote ReturnStmt\n"; 48 | } 49 | if (CallExpr *call = dyn_cast(st)) { 50 | rewriter.ReplaceText(call->getLocStart(), 7, "add5"); 51 | errs() << "** Rewrote function call\n"; 52 | } 53 | return true; 54 | } 55 | 56 | /* 57 | virtual bool VisitReturnStmt(ReturnStmt *ret) { 58 | rewriter.ReplaceText(ret->getRetValue()->getLocStart(), 6, "val"); 59 | errs() << "** Rewrote ReturnStmt\n"; 60 | return true; 61 | } 62 | 63 | virtual bool VisitCallExpr(CallExpr *call) { 64 | rewriter.ReplaceText(call->getLocStart(), 7, "add5"); 65 | errs() << "** Rewrote function call\n"; 66 | return true; 67 | } 68 | */ 69 | }; 70 | 71 | 72 | 73 | class ExampleASTConsumer : public ASTConsumer { 74 | private: 75 | ExampleVisitor *visitor; // doesn't have to be private 76 | 77 | public: 78 | // override the constructor in order to pass CI 79 | explicit ExampleASTConsumer(CompilerInstance *CI) 80 | : visitor(new ExampleVisitor(CI)) // initialize the visitor 81 | { } 82 | 83 | // override this to call our ExampleVisitor on the entire source file 84 | virtual void HandleTranslationUnit(ASTContext &Context) { 85 | /* we can use ASTContext to get the TranslationUnitDecl, which is 86 | a single Decl that collectively represents the entire source file */ 87 | visitor->TraverseDecl(Context.getTranslationUnitDecl()); 88 | } 89 | 90 | /* 91 | // override this to call our ExampleVisitor on each top-level Decl 92 | virtual bool HandleTopLevelDecl(DeclGroupRef DG) { 93 | // a DeclGroupRef may have multiple Decls, so we iterate through each one 94 | for (DeclGroupRef::iterator i = DG.begin(), e = DG.end(); i != e; i++) { 95 | Decl *D = *i; 96 | visitor->TraverseDecl(D); // recursively visit each AST node in Decl "D" 97 | } 98 | return true; 99 | } 100 | */ 101 | }; 102 | 103 | 104 | 105 | class ExampleFrontendAction : public ASTFrontendAction { 106 | public: 107 | virtual ASTConsumer *CreateASTConsumer(CompilerInstance &CI, StringRef file) { 108 | return new ExampleASTConsumer(&CI); // pass CI pointer to ASTConsumer 109 | } 110 | }; 111 | 112 | 113 | 114 | int main(int argc, const char **argv) { 115 | // parse the command-line args passed to your code 116 | CommonOptionsParser op(argc, argv); 117 | // create a new Clang Tool instance (a LibTooling environment) 118 | ClangTool Tool(op.getCompilations(), op.getSourcePathList()); 119 | 120 | // run the Clang Tool, creating a new FrontendAction (explained below) 121 | int result = Tool.run(newFrontendActionFactory()); 122 | 123 | errs() << "\nFound " << numFunctions << " functions.\n\n"; 124 | // print out the rewritten source code ("rewriter" is a global var.) 125 | rewriter.getEditBuffer(rewriter.getSourceMgr().getMainFileID()).write(errs()); 126 | return result; 127 | } 128 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CLANG_LEVEL := ../.. 2 | 3 | TOOLNAME = example #the name of your tool's executable 4 | 5 | SOURCES := Example.cpp #the Clang source files you want to compile 6 | 7 | include $(CLANG_LEVEL)/../../Makefile.config 8 | 9 | LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc option 10 | 11 | USEDLIBS = clangFrontend.a clangSerialization.a clangDriver.a \ 12 | clangTooling.a clangParse.a clangSema.a \ 13 | clangAnalysis.a clangRewriteFrontend.a clangRewriteCore.a \ 14 | clangEdit.a clangAST.a clangLex.a clangBasic.a 15 | 16 | include $(CLANG_LEVEL)/Makefile 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Clang LibTooling Example 2 | 3 | This repository includes the companion files for this [Clang LibTooling Example](http://kevinaboos.blogspot.com/2013/07/clang-tutorial-part-ii-libtooling.html). 4 | 5 | To use these files, first download and build LLVM and Clang from source. 6 | 7 | Then checkout this repository into Clang's tools directory: 8 | 9 | $ cd llvm/tools/clang/tools 10 | $ git clone https://github.com/kevinaboos/LibToolingExample.git example 11 | 12 | Then build and run the Example.cpp file: 13 | 14 | $ cd example 15 | $ make 16 | $ chmod +x run_example.sh 17 | $ ./run_example.sh 18 | -------------------------------------------------------------------------------- /run_example.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | LLVM_DIR=~/static_analysis/llvm/ #the location of your llvm dir 3 | $LLVM_DIR/Debug+Asserts/bin/example test.c -- 4 | -------------------------------------------------------------------------------- /test.c: -------------------------------------------------------------------------------- 1 | void do_math(int *x) { 2 | *x += 5; 3 | } 4 | 5 | int main(void) { 6 | int result = -1, val = 4; 7 | do_math(&val); 8 | return result; 9 | } 10 | --------------------------------------------------------------------------------