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