├── .gitignore ├── CMakeLists.txt ├── Readme.md ├── inputs ├── CMakeLists.txt ├── Turtle.cpp ├── crossed_ifs.cpp ├── macro_city.cpp ├── print_int.cpp └── square_int.cpp ├── print_local_function_decls.cpp ├── replace_if_pattern.cpp ├── simple_print_ast.cpp └── trivial_function_name_rewriter.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | *build* 2 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Thanks to this web page for the template for this cmake file 2 | ## https://heejune.me/2016/08/17/build-your-own-clang-example-outside-of-the-llvm-source-tree/ 3 | # Invoke cmake with -DCMAKE_PREFIX_PATH=/path/to/llvm/CMAKE_ROOT 4 | 5 | cmake_minimum_required (VERSION 3.0) 6 | set(CMAKE_CXX_STANDARD 14) 7 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 8 | project (Examples) 9 | 10 | 11 | ############################################################ 12 | # base 13 | ############################################################ 14 | 15 | set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "Configs" FORCE) 16 | set(CMAKE_SUPPRESS_REGENERATION TRUE) 17 | 18 | ############################################################ 19 | # setting Project informations 20 | ############################################################ 21 | 22 | set(LIBRARY_LIST clangFrontend clangSerialization clangDriver clangParse clangRewriteFrontend clangStaticAnalyzerFrontend clangSema) 23 | set(LIBRARY_LIST ${LIBRARY_LIST} clangAnalysis clangEdit clangAST clangLex clangBasic clangTooling 24 | clangRewrite clangASTMatchers clangToolingCore) 25 | set(COMPONENT_LIST mcparser bitreader support mc option) 26 | 27 | ############################################################ 28 | # generate makefiles 29 | ############################################################ 30 | 31 | find_package(LLVM REQUIRED CONFIG) 32 | message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") 33 | message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") 34 | 35 | include_directories(${LLVM_INCLUDE_DIRS}) 36 | if(LLVM_BUILD_MAIN_SRC_DIR) 37 | include_directories(${LLVM_BUILD_MAIN_SRC_DIR}/tools/clang/include) 38 | include_directories(${LLVM_BUILD_BINARY_DIR}/tools/clang/include) 39 | endif() 40 | link_directories(${LLVM_LIBRARY_DIRS}) 41 | add_definitions(${LLVM_DEFINITIONS}) 42 | 43 | add_definitions( 44 | -D__STDC_LIMIT_MACROS 45 | -D__STDC_CONSTANT_MACROS 46 | ) 47 | 48 | add_executable(simple_print_ast simple_print_ast.cpp) 49 | add_executable(print_local_function_decls print_local_function_decls.cpp) 50 | add_executable(trivial_function_name_rewriter trivial_function_name_rewriter.cpp) 51 | add_executable(replace_if_pattern replace_if_pattern.cpp) 52 | 53 | foreach(exec_name 54 | simple_print_ast print_local_function_decls trivial_function_name_rewriter 55 | replace_if_pattern) 56 | if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") 57 | foreach(link_lib IN LISTS LIBRARY_LIST) 58 | target_link_libraries(${exec_name} optimized ${link_lib}) 59 | target_link_libraries(${exec_name} debug ${link_lib}d) 60 | endforeach() 61 | else() 62 | target_link_libraries(${exec_name} ${LIBRARY_LIST}) 63 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wno-unused-parameter -fno-strict-aliasing -fno-exceptions -fno-rtti") 64 | #set(CMAKE_EXE_LINKER_FLAGS "-static -static-libgcc -static-libstdc++") 65 | endif() 66 | 67 | #llvm_map_components_to_libnames(llvm_libs ${COMPONENT_LIST}) 68 | #target_link_libraries(${exec_name} ${llvm_libs}) 69 | target_link_libraries(${exec_name} 70 | LLVMX86AsmParser # MC, MCParser, Support, X86Desc, X86Info 71 | LLVMX86Desc # MC, Support, X86AsmPrinter, X86Info 72 | LLVMX86AsmPrinter # MC, Support, X86Utils 73 | LLVMX86Info # MC, Support, Target 74 | LLVMX86Utils # Core, Support 75 | LLVMipo 76 | LLVMScalarOpts 77 | LLVMInstCombine 78 | LLVMTransformUtils 79 | LLVMAnalysis 80 | LLVMTarget 81 | LLVMOption # Support 82 | LLVMMCParser # MC, Support 83 | LLVMMC # Object, Support 84 | LLVMObject # BitReader, Core, Support 85 | LLVMBitReader # Core, Support 86 | LLVMCore # Support 87 | LLVMSupport 88 | ) 89 | endforeach() 90 | 91 | message(STATUS "User selected librarys = ${LIBRARY_LIST}") 92 | message(STATUS "User selected components = ${COMPONENT_LIST}") 93 | message(STATUS " = ${llvm_libs}") -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | ### To build 2 | mkdir inputs_build && cd inputs_build && cmake ../inputs && cd .. 3 | mkdir build && cd build 4 | cmake .. -DCMAKE_PREFIX_PATH=/path/to/llvm 5 | 6 | ### To run... 7 | The above commands build all of the examples. In the build directory, run something like: 8 | 9 | ./simple_print_ast -p=../inputs_build ../inputs/print_int.cpp 10 | 11 | Note that to support anything with includes, you're going to have to set up include paths. libTooling only knows how to find include paths when by default when you build inside of a clang layout. The easiest way to do this is to set the `CPATH` environment variable to your list of includes. Generally pretty easy on Linux or Windows, but ridiculously annoying on Mac (with my Homebrew install of clang that doesn't even include the C headers). Here's the `CPATH` I used on Mac: 12 | 13 | ```` 14 | export CPATH=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1:/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/8.1.0/include:/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk/usr/include 15 | ```` 16 | 17 | ### Web resources 18 | Information seems to be spread around, and Stack Overflow has some useful stuff, but often falls surprisingly short in concrete examples. Here are some websites that helped me put this repo together. 19 | * [clang doxygen docs](https://clang.llvm.org/doxygen/index.html). Any nontrivial usage requires using this as a reference. Fortunately, it's well-linked to the source code because, sometimes, it's required to understand the documentation (sadly). However, doxygen's insistence on hiding all of the useful classes behind a closed namespace subtree, and then continuing to re-close it every time you hit the back button is absolutely evil. 20 | * [Official clang docs to use clang as a library](http://clang.llvm.org/docs/index.html#using-clang-as-a-library). This is great when you're starting from nothing, but it leaves oh-so-many questions unanswered. 21 | * [Official clang docs matcher reference](https://clang.llvm.org/docs/LibASTMatchersReference.html). Valuable breakout of just the various matcher APIs. 22 | * [Bites, Bytes, Boos blog post](https://kevinaboos.wordpress.com/2013/07/23/clang-tutorial-part-ii-libtooling-example/), which has a nice example of an AST visitor and rewriting. [Code here](https://github.com/kevinaboos/LibToolingExample). He also talks about clang plugins. 23 | * [It's Compiling blog post](https://heejune.me/2016/08/17/build-your-own-clang-example-outside-of-the-llvm-source-tree/ 24 | ). It is amazingly tough to build libTooling code outside of the clang build system. The documentation for this is basically nonexistent. Unfortunately, I didn't notice this post until I'd largely reverse-engineered it myself, but the post did help me to clean up some of my messy, ignorant cmake. 25 | 26 | ### Additional info: 27 | * To get an AST dump, do: 28 | 29 | ```` 30 | clang++ -Xclang -ast-dump -fsyntax-only filename.cpp 31 | ```` 32 | -------------------------------------------------------------------------------- /inputs/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.0) 2 | set(CMAKE_CXX_STANDARD 14) 3 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 4 | project (Inputs) 5 | 6 | add_executable(inputs print_int.cpp square_int.cpp Turtle.cpp macro_city.cpp crossed_ifs.cpp) 7 | set (CMAKE_EXPORT_COMPILE_COMMANDS ON) 8 | -------------------------------------------------------------------------------- /inputs/Turtle.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | class Turtle 4 | { 5 | std::pair location = {0, 0}; 6 | 7 | public: 8 | void moveLeft(int distance) { location.first -= distance; } 9 | void moveRight(int distance) { location.first += distance; } 10 | void moveDown(int distance) { location.first -= distance; } 11 | void moveUp(int distance) { location.first += distance; } 12 | 13 | int getX() { return location.first; } 14 | int getY() { return location.second; } 15 | std::pair getLocation() { return location; } 16 | }; 17 | -------------------------------------------------------------------------------- /inputs/crossed_ifs.cpp: -------------------------------------------------------------------------------- 1 | bool crossed_ifs(int i, double j, char* k) 2 | { 3 | if (i == 0) 4 | return true; 5 | if (0. == j) 6 | return true; 7 | if (k == nullptr) 8 | return true; 9 | return false; 10 | } 11 | -------------------------------------------------------------------------------- /inputs/macro_city.cpp: -------------------------------------------------------------------------------- 1 | #define MY_FUNC theFunction 2 | 3 | bool MY_FUNC() 4 | { 5 | return true; 6 | } 7 | -------------------------------------------------------------------------------- /inputs/print_int.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void print_int(int val) 4 | { 5 | std::cout << val << std::endl; 6 | } -------------------------------------------------------------------------------- /inputs/square_int.cpp: -------------------------------------------------------------------------------- 1 | int square_int(int num) 2 | { 3 | return num*num; 4 | } 5 | -------------------------------------------------------------------------------- /print_local_function_decls.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "clang/AST/RecursiveASTVisitor.h" 4 | #include "clang/Frontend/CompilerInstance.h" 5 | #include "clang/Frontend/FrontendActions.h" 6 | #include "clang/Tooling/CommonOptionsParser.h" 7 | #include "clang/Tooling/Tooling.h" 8 | #include "llvm/Support/CommandLine.h" 9 | 10 | class FunctionDeclASTVisitor : public clang::RecursiveASTVisitor 11 | { 12 | clang::SourceManager& sourceManager_; 13 | 14 | public: 15 | explicit FunctionDeclASTVisitor(clang::SourceManager& sm) 16 | : sourceManager_(sm) 17 | { } 18 | 19 | virtual bool VisitFunctionDecl(clang::FunctionDecl* func) 20 | { 21 | if (sourceManager_.isWrittenInMainFile(func->getSourceRange().getBegin())) 22 | std::cout << func->getNameInfo().getName().getAsString() << std::endl; 23 | return true; 24 | } 25 | }; 26 | 27 | class FunctionDeclASTConsumer : public clang::ASTConsumer 28 | { 29 | FunctionDeclASTVisitor visitor_; // doesn't have to be private 30 | 31 | public: 32 | // override the constructor in order to pass CI 33 | explicit FunctionDeclASTConsumer(clang::CompilerInstance& ci) 34 | : visitor_(ci.getSourceManager()) 35 | { } 36 | 37 | virtual void HandleTranslationUnit(clang::ASTContext& astContext) 38 | { 39 | visitor_.TraverseDecl(astContext.getTranslationUnitDecl()); 40 | } 41 | }; 42 | 43 | class FunctionDeclFrontendAction : public clang::ASTFrontendAction 44 | { 45 | public: 46 | virtual std::unique_ptr CreateASTConsumer(clang::CompilerInstance& CI, clang::StringRef file) { 47 | return std::make_unique(CI); // pass CI pointer to ASTConsumer 48 | } 49 | }; 50 | 51 | 52 | using namespace clang::tooling; 53 | 54 | int main(int argc, const char **argv) 55 | { 56 | llvm::cl::OptionCategory MyToolCategory("print_function_decls options"); 57 | CommonOptionsParser OptionsParser(argc, argv, MyToolCategory); 58 | 59 | // Use OptionsParser.getCompilations() and OptionsParser.getSourcePathList() 60 | // to retrieve CompilationDatabase and the list of input file paths. 61 | 62 | ClangTool Tool(OptionsParser.getCompilations(), OptionsParser.getSourcePathList()); 63 | return Tool.run(newFrontendActionFactory().get()); 64 | } 65 | 66 | -------------------------------------------------------------------------------- /replace_if_pattern.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "clang/AST/RecursiveASTVisitor.h" 5 | #include "clang/ASTMatchers/ASTMatchFinder.h" 6 | #include "clang/Frontend/CompilerInstance.h" 7 | #include "clang/Frontend/FrontendActions.h" 8 | #include "clang/Lex/Lexer.h" 9 | #include "clang/Rewrite/Core/Rewriter.h" 10 | #include "clang/Tooling/CommonOptionsParser.h" 11 | #include "clang/Tooling/Refactoring.h" 12 | #include "clang/Tooling/RefactoringCallbacks.h" 13 | #include "clang/Tooling/Tooling.h" 14 | #include "llvm/Support/CommandLine.h" 15 | 16 | 17 | using namespace clang::ast_matchers; 18 | using namespace clang::tooling; 19 | 20 | class IfStatementRewriter : public MatchFinder::MatchCallback 21 | { 22 | clang::Rewriter rewriter_; 23 | 24 | public : 25 | virtual void run(const MatchFinder::MatchResult &result) 26 | { 27 | clang::SourceManager* sourceManager = result.SourceManager; 28 | const clang::LangOptions& langOptions = result.Context->getLangOpts(); 29 | rewriter_.setSourceMgr(*sourceManager, langOptions); 30 | const clang::Stmt* ifStmt = result.Nodes.getNodeAs("ifStatement"); 31 | const clang::Stmt* rhs = result.Nodes.getNodeAs("rhs"); 32 | const clang::Stmt* lhs = result.Nodes.getNodeAs("lhs"); 33 | 34 | if (ifStmt && rhs && lhs) 35 | { 36 | clang::CharSourceRange lhsRange = clang::CharSourceRange::getTokenRange(lhs->getSourceRange()); 37 | clang::CharSourceRange rhsRange = clang::CharSourceRange::getTokenRange(rhs->getSourceRange()); 38 | 39 | std::string lhsString = clang::Lexer::getSourceText(lhsRange, *sourceManager, langOptions).str(); 40 | std::string rhsString = clang::Lexer::getSourceText(rhsRange, *sourceManager, langOptions).str(); 41 | 42 | Replacements replacements; 43 | replacements.add(Replacement(*sourceManager, lhsRange, rhsString)); 44 | replacements.add(Replacement(*sourceManager, rhsRange, lhsString)); 45 | applyAllReplacements(replacements, rewriter_); 46 | } 47 | } 48 | }; 49 | 50 | 51 | 52 | int main(int argc, const char **argv) 53 | { 54 | llvm::cl::OptionCategory MyToolCategory("replace_for_pattern options"); 55 | CommonOptionsParser OptionsParser(argc, argv, MyToolCategory); 56 | ClangTool Tool(OptionsParser.getCompilations(), OptionsParser.getSourcePathList()); 57 | 58 | // Matches 'if (nonliteral == literal)' 59 | StatementMatcher ifStatementMatcher = 60 | ifStmt(binaryOperator( 61 | hasOperatorName("=="), 62 | hasLHS(expr().bind("lhs")), 63 | hasRHS(userDefinedLiteral().bind("rhs")))).bind("ifStatement"); 64 | 65 | IfStatementRewriter rewriter; 66 | MatchFinder finder; 67 | finder.addMatcher(ifStatementMatcher, &rewriter); 68 | 69 | return Tool.run(newFrontendActionFactory(&finder).get()); 70 | } 71 | 72 | -------------------------------------------------------------------------------- /simple_print_ast.cpp: -------------------------------------------------------------------------------- 1 | #include "clang/Frontend/FrontendActions.h" 2 | #include "clang/Tooling/CommonOptionsParser.h" 3 | #include "clang/Tooling/Tooling.h" 4 | #include "llvm/Support/CommandLine.h" 5 | 6 | using namespace clang::tooling; 7 | 8 | 9 | int main(int argc, const char **argv) 10 | { 11 | llvm::cl::OptionCategory MyToolCategory("simple_print_ast options"); 12 | CommonOptionsParser OptionsParser(argc, argv, MyToolCategory); 13 | 14 | // Use OptionsParser.getCompilations() and OptionsParser.getSourcePathList() 15 | // to retrieve CompilationDatabase and the list of input file paths. 16 | 17 | ClangTool Tool(OptionsParser.getCompilations(), OptionsParser.getSourcePathList()); 18 | return Tool.run(newFrontendActionFactory().get()); 19 | } 20 | 21 | -------------------------------------------------------------------------------- /trivial_function_name_rewriter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "clang/AST/RecursiveASTVisitor.h" 4 | #include "clang/Frontend/CompilerInstance.h" 5 | #include "clang/Frontend/FrontendActions.h" 6 | #include "clang/Rewrite/Core/Rewriter.h" 7 | #include "clang/Tooling/CommonOptionsParser.h" 8 | #include "clang/Tooling/Tooling.h" 9 | #include "llvm/Support/CommandLine.h" 10 | 11 | class FunctionDeclASTVisitor : public clang::RecursiveASTVisitor 12 | { 13 | clang::SourceManager& sourceManager_; 14 | clang::Rewriter& rewriter_; 15 | 16 | public: 17 | explicit FunctionDeclASTVisitor(clang::SourceManager& sm, clang::Rewriter& r) 18 | : sourceManager_(sm), rewriter_(r) 19 | { } 20 | 21 | virtual bool VisitFunctionDecl(clang::FunctionDecl* func) 22 | { 23 | if (sourceManager_.isWrittenInMainFile(func->getSourceRange().getBegin())) 24 | { 25 | std::string oldFuncName = func->getNameInfo().getName().getAsString(); 26 | std::string newFuncName = oldFuncName + "_x"; 27 | std::cout << oldFuncName << " -> " << newFuncName << std::endl; 28 | rewriter_.ReplaceText(func->getNameInfo().getSourceRange(), newFuncName); 29 | } 30 | return true; 31 | } 32 | }; 33 | 34 | class FunctionDeclASTConsumer : public clang::ASTConsumer 35 | { 36 | clang::Rewriter rewriter_; 37 | FunctionDeclASTVisitor visitor_; // doesn't have to be private 38 | 39 | public: 40 | // override the constructor in order to pass CI 41 | explicit FunctionDeclASTConsumer(clang::CompilerInstance& ci) 42 | : visitor_(ci.getSourceManager(), rewriter_) 43 | { } 44 | 45 | virtual void HandleTranslationUnit(clang::ASTContext& astContext) 46 | { 47 | rewriter_.setSourceMgr(astContext.getSourceManager(), astContext.getLangOpts()); 48 | visitor_.TraverseDecl(astContext.getTranslationUnitDecl()); 49 | rewriter_.overwriteChangedFiles(); 50 | } 51 | }; 52 | 53 | class FunctionDeclFrontendAction : public clang::ASTFrontendAction 54 | { 55 | public: 56 | virtual std::unique_ptr CreateASTConsumer(clang::CompilerInstance& CI, clang::StringRef file) { 57 | return std::make_unique(CI); // pass CI pointer to ASTConsumer 58 | } 59 | }; 60 | 61 | 62 | using namespace clang::tooling; 63 | 64 | int main(int argc, const char **argv) 65 | { 66 | llvm::cl::OptionCategory MyToolCategory("print_function_decls options"); 67 | CommonOptionsParser OptionsParser(argc, argv, MyToolCategory); 68 | 69 | // Use OptionsParser.getCompilations() and OptionsParser.getSourcePathList() 70 | // to retrieve CompilationDatabase and the list of input file paths. 71 | 72 | ClangTool Tool(OptionsParser.getCompilations(), OptionsParser.getSourcePathList()); 73 | return Tool.run(newFrontendActionFactory().get()); 74 | } 75 | 76 | --------------------------------------------------------------------------------