1*a75b2cacSAlex Lorenz //===- ClangDiff.cpp - compare source files by AST nodes ------*- C++ -*- -===// 2*a75b2cacSAlex Lorenz // 3*a75b2cacSAlex Lorenz // The LLVM Compiler Infrastructure 4*a75b2cacSAlex Lorenz // 5*a75b2cacSAlex Lorenz // This file is distributed under the University of Illinois Open Source 6*a75b2cacSAlex Lorenz // License. See LICENSE.TXT for details. 7*a75b2cacSAlex Lorenz // 8*a75b2cacSAlex Lorenz //===----------------------------------------------------------------------===// 9*a75b2cacSAlex Lorenz // 10*a75b2cacSAlex Lorenz // This file implements a tool for syntax tree based comparison using 11*a75b2cacSAlex Lorenz // Tooling/ASTDiff. 12*a75b2cacSAlex Lorenz // 13*a75b2cacSAlex Lorenz //===----------------------------------------------------------------------===// 14*a75b2cacSAlex Lorenz 15*a75b2cacSAlex Lorenz #include "clang/Tooling/ASTDiff/ASTDiff.h" 16*a75b2cacSAlex Lorenz #include "clang/Tooling/CommonOptionsParser.h" 17*a75b2cacSAlex Lorenz #include "clang/Tooling/Tooling.h" 18*a75b2cacSAlex Lorenz #include "llvm/Support/CommandLine.h" 19*a75b2cacSAlex Lorenz 20*a75b2cacSAlex Lorenz using namespace llvm; 21*a75b2cacSAlex Lorenz using namespace clang; 22*a75b2cacSAlex Lorenz using namespace clang::tooling; 23*a75b2cacSAlex Lorenz 24*a75b2cacSAlex Lorenz static cl::OptionCategory ClangDiffCategory("clang-diff options"); 25*a75b2cacSAlex Lorenz 26*a75b2cacSAlex Lorenz static cl::opt<bool> 27*a75b2cacSAlex Lorenz DumpAST("ast-dump", 28*a75b2cacSAlex Lorenz cl::desc("Print the internal representation of the AST as JSON."), 29*a75b2cacSAlex Lorenz cl::init(false), cl::cat(ClangDiffCategory)); 30*a75b2cacSAlex Lorenz 31*a75b2cacSAlex Lorenz static cl::opt<bool> NoCompilationDatabase( 32*a75b2cacSAlex Lorenz "no-compilation-database", 33*a75b2cacSAlex Lorenz cl::desc( 34*a75b2cacSAlex Lorenz "Do not attempt to load build settings from a compilation database"), 35*a75b2cacSAlex Lorenz cl::init(false), cl::cat(ClangDiffCategory)); 36*a75b2cacSAlex Lorenz 37*a75b2cacSAlex Lorenz static cl::opt<std::string> SourcePath(cl::Positional, cl::desc("<source>"), 38*a75b2cacSAlex Lorenz cl::Required, 39*a75b2cacSAlex Lorenz cl::cat(ClangDiffCategory)); 40*a75b2cacSAlex Lorenz 41*a75b2cacSAlex Lorenz static cl::opt<std::string> DestinationPath(cl::Positional, 42*a75b2cacSAlex Lorenz cl::desc("<destination>"), 43*a75b2cacSAlex Lorenz cl::Optional, 44*a75b2cacSAlex Lorenz cl::cat(ClangDiffCategory)); 45*a75b2cacSAlex Lorenz 46*a75b2cacSAlex Lorenz static std::unique_ptr<ASTUnit> getAST(const StringRef Filename) { 47*a75b2cacSAlex Lorenz std::string ErrorMessage; 48*a75b2cacSAlex Lorenz std::unique_ptr<CompilationDatabase> Compilations; 49*a75b2cacSAlex Lorenz if (!NoCompilationDatabase) 50*a75b2cacSAlex Lorenz Compilations = 51*a75b2cacSAlex Lorenz CompilationDatabase::autoDetectFromSource(Filename, ErrorMessage); 52*a75b2cacSAlex Lorenz if (!Compilations) { 53*a75b2cacSAlex Lorenz if (!NoCompilationDatabase) 54*a75b2cacSAlex Lorenz llvm::errs() 55*a75b2cacSAlex Lorenz << "Error while trying to load a compilation database, running " 56*a75b2cacSAlex Lorenz "without flags.\n" 57*a75b2cacSAlex Lorenz << ErrorMessage; 58*a75b2cacSAlex Lorenz Compilations = llvm::make_unique<clang::tooling::FixedCompilationDatabase>( 59*a75b2cacSAlex Lorenz ".", std::vector<std::string>()); 60*a75b2cacSAlex Lorenz } 61*a75b2cacSAlex Lorenz std::array<std::string, 1> Files = {{Filename}}; 62*a75b2cacSAlex Lorenz ClangTool Tool(*Compilations, Files); 63*a75b2cacSAlex Lorenz std::vector<std::unique_ptr<ASTUnit>> ASTs; 64*a75b2cacSAlex Lorenz Tool.buildASTs(ASTs); 65*a75b2cacSAlex Lorenz if (ASTs.size() != Files.size()) 66*a75b2cacSAlex Lorenz return nullptr; 67*a75b2cacSAlex Lorenz return std::move(ASTs[0]); 68*a75b2cacSAlex Lorenz } 69*a75b2cacSAlex Lorenz 70*a75b2cacSAlex Lorenz int main(int argc, const char **argv) { 71*a75b2cacSAlex Lorenz cl::HideUnrelatedOptions(ClangDiffCategory); 72*a75b2cacSAlex Lorenz if (!cl::ParseCommandLineOptions(argc, argv)) { 73*a75b2cacSAlex Lorenz cl::PrintOptionValues(); 74*a75b2cacSAlex Lorenz return 1; 75*a75b2cacSAlex Lorenz } 76*a75b2cacSAlex Lorenz 77*a75b2cacSAlex Lorenz if (DumpAST) { 78*a75b2cacSAlex Lorenz if (!DestinationPath.empty()) { 79*a75b2cacSAlex Lorenz llvm::errs() << "Error: Please specify exactly one filename.\n"; 80*a75b2cacSAlex Lorenz return 1; 81*a75b2cacSAlex Lorenz } 82*a75b2cacSAlex Lorenz std::unique_ptr<ASTUnit> AST = getAST(SourcePath); 83*a75b2cacSAlex Lorenz if (!AST) 84*a75b2cacSAlex Lorenz return 1; 85*a75b2cacSAlex Lorenz diff::SyntaxTree Tree(AST->getASTContext()); 86*a75b2cacSAlex Lorenz Tree.printAsJson(llvm::outs()); 87*a75b2cacSAlex Lorenz return 0; 88*a75b2cacSAlex Lorenz } 89*a75b2cacSAlex Lorenz 90*a75b2cacSAlex Lorenz if (DestinationPath.empty()) { 91*a75b2cacSAlex Lorenz llvm::errs() << "Error: Exactly two paths are required.\n"; 92*a75b2cacSAlex Lorenz return 1; 93*a75b2cacSAlex Lorenz } 94*a75b2cacSAlex Lorenz 95*a75b2cacSAlex Lorenz std::unique_ptr<ASTUnit> Src = getAST(SourcePath); 96*a75b2cacSAlex Lorenz std::unique_ptr<ASTUnit> Dst = getAST(DestinationPath); 97*a75b2cacSAlex Lorenz if (!Src || !Dst) 98*a75b2cacSAlex Lorenz return 1; 99*a75b2cacSAlex Lorenz 100*a75b2cacSAlex Lorenz diff::ComparisonOptions Options; 101*a75b2cacSAlex Lorenz diff::SyntaxTree SrcTree(Src->getASTContext()); 102*a75b2cacSAlex Lorenz diff::SyntaxTree DstTree(Dst->getASTContext()); 103*a75b2cacSAlex Lorenz diff::ASTDiff DiffTool(SrcTree, DstTree, Options); 104*a75b2cacSAlex Lorenz for (const auto &Match : DiffTool.getMatches()) 105*a75b2cacSAlex Lorenz DiffTool.printMatch(llvm::outs(), Match); 106*a75b2cacSAlex Lorenz for (const auto &Change : DiffTool.getChanges()) 107*a75b2cacSAlex Lorenz DiffTool.printChange(llvm::outs(), Change); 108*a75b2cacSAlex Lorenz 109*a75b2cacSAlex Lorenz return 0; 110*a75b2cacSAlex Lorenz } 111