1 //===- ClangDiff.cpp - compare source files by AST nodes ------*- C++ -*- -===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // This file implements a tool for syntax tree based comparison using 11 // Tooling/ASTDiff. 12 // 13 //===----------------------------------------------------------------------===// 14 15 #include "clang/Tooling/ASTDiff/ASTDiff.h" 16 #include "clang/Tooling/CommonOptionsParser.h" 17 #include "clang/Tooling/Tooling.h" 18 #include "llvm/Support/CommandLine.h" 19 20 using namespace llvm; 21 using namespace clang; 22 using namespace clang::tooling; 23 24 static cl::OptionCategory ClangDiffCategory("clang-diff options"); 25 26 static cl::opt<bool> 27 ASTDump("ast-dump", 28 cl::desc("Print the internal representation of the AST as JSON."), 29 cl::init(false), cl::cat(ClangDiffCategory)); 30 31 static cl::opt<std::string> SourcePath(cl::Positional, cl::desc("<source>"), 32 cl::Required, 33 cl::cat(ClangDiffCategory)); 34 35 static cl::opt<std::string> DestinationPath(cl::Positional, 36 cl::desc("<destination>"), 37 cl::Optional, 38 cl::cat(ClangDiffCategory)); 39 40 static cl::opt<int> MaxSize("s", cl::desc("<maxsize>"), cl::Optional, 41 cl::init(-1), cl::cat(ClangDiffCategory)); 42 43 static cl::opt<std::string> BuildPath("p", cl::desc("Build path"), cl::init(""), 44 cl::Optional, cl::cat(ClangDiffCategory)); 45 46 static cl::list<std::string> ArgsAfter( 47 "extra-arg", 48 cl::desc("Additional argument to append to the compiler command line"), 49 cl::cat(ClangDiffCategory)); 50 51 static cl::list<std::string> ArgsBefore( 52 "extra-arg-before", 53 cl::desc("Additional argument to prepend to the compiler command line"), 54 cl::cat(ClangDiffCategory)); 55 56 static void addExtraArgs(std::unique_ptr<CompilationDatabase> &Compilations) { 57 if (!Compilations) 58 return; 59 auto AdjustingCompilations = 60 llvm::make_unique<ArgumentsAdjustingCompilations>( 61 std::move(Compilations)); 62 AdjustingCompilations->appendArgumentsAdjuster( 63 getInsertArgumentAdjuster(ArgsBefore, ArgumentInsertPosition::BEGIN)); 64 AdjustingCompilations->appendArgumentsAdjuster( 65 getInsertArgumentAdjuster(ArgsAfter, ArgumentInsertPosition::END)); 66 Compilations = std::move(AdjustingCompilations); 67 } 68 69 static std::unique_ptr<ASTUnit> 70 getAST(const std::unique_ptr<CompilationDatabase> &CommonCompilations, 71 const StringRef Filename) { 72 std::string ErrorMessage; 73 std::unique_ptr<CompilationDatabase> Compilations; 74 if (!CommonCompilations) { 75 Compilations = CompilationDatabase::autoDetectFromSource( 76 BuildPath.empty() ? Filename : BuildPath, ErrorMessage); 77 if (!Compilations) { 78 llvm::errs() 79 << "Error while trying to load a compilation database, running " 80 "without flags.\n" 81 << ErrorMessage; 82 Compilations = 83 llvm::make_unique<clang::tooling::FixedCompilationDatabase>( 84 ".", std::vector<std::string>()); 85 } 86 } 87 addExtraArgs(Compilations); 88 std::array<std::string, 1> Files = {{Filename}}; 89 ClangTool Tool(Compilations ? *Compilations : *CommonCompilations, Files); 90 std::vector<std::unique_ptr<ASTUnit>> ASTs; 91 Tool.buildASTs(ASTs); 92 if (ASTs.size() != Files.size()) 93 return nullptr; 94 return std::move(ASTs[0]); 95 } 96 97 int main(int argc, const char **argv) { 98 std::string ErrorMessage; 99 std::unique_ptr<CompilationDatabase> CommonCompilations = 100 FixedCompilationDatabase::loadFromCommandLine(argc, argv, ErrorMessage); 101 if (!CommonCompilations && !ErrorMessage.empty()) 102 llvm::errs() << ErrorMessage; 103 cl::HideUnrelatedOptions(ClangDiffCategory); 104 if (!cl::ParseCommandLineOptions(argc, argv)) { 105 cl::PrintOptionValues(); 106 return 1; 107 } 108 109 addExtraArgs(CommonCompilations); 110 111 if (ASTDump) { 112 if (!DestinationPath.empty()) { 113 llvm::errs() << "Error: Please specify exactly one filename.\n"; 114 return 1; 115 } 116 std::unique_ptr<ASTUnit> AST = getAST(CommonCompilations, SourcePath); 117 if (!AST) 118 return 1; 119 diff::SyntaxTree Tree(AST->getASTContext()); 120 Tree.printAsJson(llvm::outs()); 121 return 0; 122 } 123 124 if (DestinationPath.empty()) { 125 llvm::errs() << "Error: Exactly two paths are required.\n"; 126 return 1; 127 } 128 129 std::unique_ptr<ASTUnit> Src = getAST(CommonCompilations, SourcePath); 130 std::unique_ptr<ASTUnit> Dst = getAST(CommonCompilations, DestinationPath); 131 if (!Src || !Dst) 132 return 1; 133 134 diff::ComparisonOptions Options; 135 if (MaxSize != -1) 136 Options.MaxSize = MaxSize; 137 diff::SyntaxTree SrcTree(Src->getASTContext()); 138 diff::SyntaxTree DstTree(Dst->getASTContext()); 139 diff::ASTDiff DiffTool(SrcTree, DstTree, Options); 140 for (const auto &Match : DiffTool.getMatches()) 141 DiffTool.printMatch(llvm::outs(), Match); 142 for (const auto &Change : DiffTool.getChanges()) 143 DiffTool.printChange(llvm::outs(), Change); 144 145 return 0; 146 } 147