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> 27914a958eSJohannes Altmanninger ASTDump("ast-dump", 28a1d2b5d5SJohannes Altmanninger cl::desc("Print the internal representation of the AST."), 29a1d2b5d5SJohannes Altmanninger cl::init(false), cl::cat(ClangDiffCategory)); 30a1d2b5d5SJohannes Altmanninger 31a1d2b5d5SJohannes Altmanninger static cl::opt<bool> ASTDumpJson( 32a1d2b5d5SJohannes Altmanninger "ast-dump-json", 33a75b2cacSAlex Lorenz cl::desc("Print the internal representation of the AST as JSON."), 34a75b2cacSAlex Lorenz cl::init(false), cl::cat(ClangDiffCategory)); 35a75b2cacSAlex Lorenz 36683876caSJohannes Altmanninger static cl::opt<bool> 37683876caSJohannes Altmanninger PrintMatches("dump-matches", cl::desc("Print the matched nodes."), 38683876caSJohannes Altmanninger cl::init(false), cl::cat(ClangDiffCategory)); 39683876caSJohannes Altmanninger 40*a29d6aecSJohannes Altmanninger static cl::opt<bool> HtmlDiff("html", 41*a29d6aecSJohannes Altmanninger cl::desc("Output a side-by-side diff in HTML."), 42*a29d6aecSJohannes Altmanninger cl::init(false), cl::cat(ClangDiffCategory)); 43*a29d6aecSJohannes Altmanninger 44a75b2cacSAlex Lorenz static cl::opt<std::string> SourcePath(cl::Positional, cl::desc("<source>"), 45a75b2cacSAlex Lorenz cl::Required, 46a75b2cacSAlex Lorenz cl::cat(ClangDiffCategory)); 47a75b2cacSAlex Lorenz 48a75b2cacSAlex Lorenz static cl::opt<std::string> DestinationPath(cl::Positional, 49a75b2cacSAlex Lorenz cl::desc("<destination>"), 50a75b2cacSAlex Lorenz cl::Optional, 51a75b2cacSAlex Lorenz cl::cat(ClangDiffCategory)); 52a75b2cacSAlex Lorenz 53849f20e4SJohannes Altmanninger static cl::opt<int> MaxSize("s", cl::desc("<maxsize>"), cl::Optional, 54849f20e4SJohannes Altmanninger cl::init(-1), cl::cat(ClangDiffCategory)); 55849f20e4SJohannes Altmanninger 56849f20e4SJohannes Altmanninger static cl::opt<std::string> BuildPath("p", cl::desc("Build path"), cl::init(""), 57849f20e4SJohannes Altmanninger cl::Optional, cl::cat(ClangDiffCategory)); 58849f20e4SJohannes Altmanninger 59849f20e4SJohannes Altmanninger static cl::list<std::string> ArgsAfter( 60849f20e4SJohannes Altmanninger "extra-arg", 61849f20e4SJohannes Altmanninger cl::desc("Additional argument to append to the compiler command line"), 62849f20e4SJohannes Altmanninger cl::cat(ClangDiffCategory)); 63849f20e4SJohannes Altmanninger 64849f20e4SJohannes Altmanninger static cl::list<std::string> ArgsBefore( 65849f20e4SJohannes Altmanninger "extra-arg-before", 66849f20e4SJohannes Altmanninger cl::desc("Additional argument to prepend to the compiler command line"), 67849f20e4SJohannes Altmanninger cl::cat(ClangDiffCategory)); 68849f20e4SJohannes Altmanninger 69849f20e4SJohannes Altmanninger static void addExtraArgs(std::unique_ptr<CompilationDatabase> &Compilations) { 70849f20e4SJohannes Altmanninger if (!Compilations) 71849f20e4SJohannes Altmanninger return; 72849f20e4SJohannes Altmanninger auto AdjustingCompilations = 73849f20e4SJohannes Altmanninger llvm::make_unique<ArgumentsAdjustingCompilations>( 74849f20e4SJohannes Altmanninger std::move(Compilations)); 75849f20e4SJohannes Altmanninger AdjustingCompilations->appendArgumentsAdjuster( 76849f20e4SJohannes Altmanninger getInsertArgumentAdjuster(ArgsBefore, ArgumentInsertPosition::BEGIN)); 77849f20e4SJohannes Altmanninger AdjustingCompilations->appendArgumentsAdjuster( 78849f20e4SJohannes Altmanninger getInsertArgumentAdjuster(ArgsAfter, ArgumentInsertPosition::END)); 79849f20e4SJohannes Altmanninger Compilations = std::move(AdjustingCompilations); 80849f20e4SJohannes Altmanninger } 81849f20e4SJohannes Altmanninger 82849f20e4SJohannes Altmanninger static std::unique_ptr<ASTUnit> 83849f20e4SJohannes Altmanninger getAST(const std::unique_ptr<CompilationDatabase> &CommonCompilations, 84849f20e4SJohannes Altmanninger const StringRef Filename) { 85a75b2cacSAlex Lorenz std::string ErrorMessage; 86a75b2cacSAlex Lorenz std::unique_ptr<CompilationDatabase> Compilations; 87849f20e4SJohannes Altmanninger if (!CommonCompilations) { 88849f20e4SJohannes Altmanninger Compilations = CompilationDatabase::autoDetectFromSource( 89849f20e4SJohannes Altmanninger BuildPath.empty() ? Filename : BuildPath, ErrorMessage); 90a75b2cacSAlex Lorenz if (!Compilations) { 91a75b2cacSAlex Lorenz llvm::errs() 92a75b2cacSAlex Lorenz << "Error while trying to load a compilation database, running " 93a75b2cacSAlex Lorenz "without flags.\n" 94a75b2cacSAlex Lorenz << ErrorMessage; 95849f20e4SJohannes Altmanninger Compilations = 96849f20e4SJohannes Altmanninger llvm::make_unique<clang::tooling::FixedCompilationDatabase>( 97a75b2cacSAlex Lorenz ".", std::vector<std::string>()); 98a75b2cacSAlex Lorenz } 99849f20e4SJohannes Altmanninger } 100849f20e4SJohannes Altmanninger addExtraArgs(Compilations); 101a75b2cacSAlex Lorenz std::array<std::string, 1> Files = {{Filename}}; 102849f20e4SJohannes Altmanninger ClangTool Tool(Compilations ? *Compilations : *CommonCompilations, Files); 103a75b2cacSAlex Lorenz std::vector<std::unique_ptr<ASTUnit>> ASTs; 104a75b2cacSAlex Lorenz Tool.buildASTs(ASTs); 105a75b2cacSAlex Lorenz if (ASTs.size() != Files.size()) 106a75b2cacSAlex Lorenz return nullptr; 107a75b2cacSAlex Lorenz return std::move(ASTs[0]); 108a75b2cacSAlex Lorenz } 109a75b2cacSAlex Lorenz 1100da12c84SJohannes Altmanninger static char hexdigit(int N) { return N &= 0xf, N + (N < 10 ? '0' : 'a' - 10); } 1110da12c84SJohannes Altmanninger 112*a29d6aecSJohannes Altmanninger static const char HtmlDiffHeader[] = R"( 113*a29d6aecSJohannes Altmanninger <html> 114*a29d6aecSJohannes Altmanninger <head> 115*a29d6aecSJohannes Altmanninger <meta charset='utf-8'/> 116*a29d6aecSJohannes Altmanninger <style> 117*a29d6aecSJohannes Altmanninger span.d { color: red; } 118*a29d6aecSJohannes Altmanninger span.u { color: #cc00cc; } 119*a29d6aecSJohannes Altmanninger span.i { color: green; } 120*a29d6aecSJohannes Altmanninger span.m { font-weight: bold; } 121*a29d6aecSJohannes Altmanninger span { font-weight: normal; color: black; } 122*a29d6aecSJohannes Altmanninger div.code { 123*a29d6aecSJohannes Altmanninger width: 48%; 124*a29d6aecSJohannes Altmanninger height: 98%; 125*a29d6aecSJohannes Altmanninger overflow: scroll; 126*a29d6aecSJohannes Altmanninger float: left; 127*a29d6aecSJohannes Altmanninger padding: 0 0 0.5% 0.5%; 128*a29d6aecSJohannes Altmanninger border: solid 2px LightGrey; 129*a29d6aecSJohannes Altmanninger border-radius: 5px; 130*a29d6aecSJohannes Altmanninger } 131*a29d6aecSJohannes Altmanninger </style> 132*a29d6aecSJohannes Altmanninger </head> 133*a29d6aecSJohannes Altmanninger <script type='text/javascript'> 134*a29d6aecSJohannes Altmanninger highlightStack = [] 135*a29d6aecSJohannes Altmanninger function clearHighlight() { 136*a29d6aecSJohannes Altmanninger while (highlightStack.length) { 137*a29d6aecSJohannes Altmanninger let [l, r] = highlightStack.pop() 138*a29d6aecSJohannes Altmanninger document.getElementById(l).style.backgroundColor = 'white' 139*a29d6aecSJohannes Altmanninger document.getElementById(r).style.backgroundColor = 'white' 140*a29d6aecSJohannes Altmanninger } 141*a29d6aecSJohannes Altmanninger } 142*a29d6aecSJohannes Altmanninger function highlight(event) { 143*a29d6aecSJohannes Altmanninger id = event.target['id'] 144*a29d6aecSJohannes Altmanninger doHighlight(id) 145*a29d6aecSJohannes Altmanninger } 146*a29d6aecSJohannes Altmanninger function doHighlight(id) { 147*a29d6aecSJohannes Altmanninger clearHighlight() 148*a29d6aecSJohannes Altmanninger source = document.getElementById(id) 149*a29d6aecSJohannes Altmanninger if (!source.attributes['tid']) 150*a29d6aecSJohannes Altmanninger return 151*a29d6aecSJohannes Altmanninger tid = source.attributes['tid'].value 152*a29d6aecSJohannes Altmanninger target = document.getElementById(tid) 153*a29d6aecSJohannes Altmanninger if (!target || source.parentElement && source.parentElement.classList.contains('code')) 154*a29d6aecSJohannes Altmanninger return 155*a29d6aecSJohannes Altmanninger source.style.backgroundColor = target.style.backgroundColor = 'lightgrey' 156*a29d6aecSJohannes Altmanninger highlightStack.push([id, tid]) 157*a29d6aecSJohannes Altmanninger source.scrollIntoView() 158*a29d6aecSJohannes Altmanninger target.scrollIntoView() 159*a29d6aecSJohannes Altmanninger location.hash = '#' + id 160*a29d6aecSJohannes Altmanninger } 161*a29d6aecSJohannes Altmanninger function scrollToBoth() { 162*a29d6aecSJohannes Altmanninger doHighlight(location.hash.substr(1)) 163*a29d6aecSJohannes Altmanninger } 164*a29d6aecSJohannes Altmanninger window.onload = scrollToBoth 165*a29d6aecSJohannes Altmanninger </script> 166*a29d6aecSJohannes Altmanninger <body> 167*a29d6aecSJohannes Altmanninger <div onclick='highlight(event)'> 168*a29d6aecSJohannes Altmanninger )"; 169*a29d6aecSJohannes Altmanninger 170*a29d6aecSJohannes Altmanninger static void printHtml(raw_ostream &OS, char C) { 171*a29d6aecSJohannes Altmanninger switch (C) { 172*a29d6aecSJohannes Altmanninger case '&': 173*a29d6aecSJohannes Altmanninger OS << "&"; 174*a29d6aecSJohannes Altmanninger break; 175*a29d6aecSJohannes Altmanninger case '<': 176*a29d6aecSJohannes Altmanninger OS << "<"; 177*a29d6aecSJohannes Altmanninger break; 178*a29d6aecSJohannes Altmanninger case '>': 179*a29d6aecSJohannes Altmanninger OS << ">"; 180*a29d6aecSJohannes Altmanninger break; 181*a29d6aecSJohannes Altmanninger case '\'': 182*a29d6aecSJohannes Altmanninger OS << "'"; 183*a29d6aecSJohannes Altmanninger break; 184*a29d6aecSJohannes Altmanninger case '"': 185*a29d6aecSJohannes Altmanninger OS << """; 186*a29d6aecSJohannes Altmanninger break; 187*a29d6aecSJohannes Altmanninger default: 188*a29d6aecSJohannes Altmanninger OS << C; 189*a29d6aecSJohannes Altmanninger } 190*a29d6aecSJohannes Altmanninger } 191*a29d6aecSJohannes Altmanninger 192*a29d6aecSJohannes Altmanninger static void printHtml(raw_ostream &OS, const StringRef Str) { 193*a29d6aecSJohannes Altmanninger for (char C : Str) 194*a29d6aecSJohannes Altmanninger printHtml(OS, C); 195*a29d6aecSJohannes Altmanninger } 196*a29d6aecSJohannes Altmanninger 197*a29d6aecSJohannes Altmanninger static std::string getChangeKindAbbr(diff::ChangeKind Kind) { 198*a29d6aecSJohannes Altmanninger switch (Kind) { 199*a29d6aecSJohannes Altmanninger case diff::None: 200*a29d6aecSJohannes Altmanninger return ""; 201*a29d6aecSJohannes Altmanninger case diff::Delete: 202*a29d6aecSJohannes Altmanninger return "d"; 203*a29d6aecSJohannes Altmanninger case diff::Update: 204*a29d6aecSJohannes Altmanninger return "u"; 205*a29d6aecSJohannes Altmanninger case diff::Insert: 206*a29d6aecSJohannes Altmanninger return "i"; 207*a29d6aecSJohannes Altmanninger case diff::Move: 208*a29d6aecSJohannes Altmanninger return "m"; 209*a29d6aecSJohannes Altmanninger case diff::UpdateMove: 210*a29d6aecSJohannes Altmanninger return "u m"; 211*a29d6aecSJohannes Altmanninger } 212*a29d6aecSJohannes Altmanninger } 213*a29d6aecSJohannes Altmanninger 214*a29d6aecSJohannes Altmanninger static unsigned printHtmlForNode(raw_ostream &OS, const diff::ASTDiff &Diff, 215*a29d6aecSJohannes Altmanninger diff::SyntaxTree &Tree, bool IsLeft, 216*a29d6aecSJohannes Altmanninger diff::NodeId Id, unsigned Offset) { 217*a29d6aecSJohannes Altmanninger const diff::Node &Node = Tree.getNode(Id); 218*a29d6aecSJohannes Altmanninger char MyTag, OtherTag; 219*a29d6aecSJohannes Altmanninger diff::NodeId LeftId, RightId; 220*a29d6aecSJohannes Altmanninger diff::NodeId TargetId = Diff.getMapped(Tree, Id); 221*a29d6aecSJohannes Altmanninger if (IsLeft) { 222*a29d6aecSJohannes Altmanninger MyTag = 'L'; 223*a29d6aecSJohannes Altmanninger OtherTag = 'R'; 224*a29d6aecSJohannes Altmanninger LeftId = Id; 225*a29d6aecSJohannes Altmanninger RightId = TargetId; 226*a29d6aecSJohannes Altmanninger } else { 227*a29d6aecSJohannes Altmanninger MyTag = 'R'; 228*a29d6aecSJohannes Altmanninger OtherTag = 'L'; 229*a29d6aecSJohannes Altmanninger LeftId = TargetId; 230*a29d6aecSJohannes Altmanninger RightId = Id; 231*a29d6aecSJohannes Altmanninger } 232*a29d6aecSJohannes Altmanninger unsigned Begin, End; 233*a29d6aecSJohannes Altmanninger std::tie(Begin, End) = Tree.getSourceRangeOffsets(Node); 234*a29d6aecSJohannes Altmanninger const SourceManager &SrcMgr = Tree.getASTContext().getSourceManager(); 235*a29d6aecSJohannes Altmanninger auto Code = SrcMgr.getBuffer(SrcMgr.getMainFileID())->getBuffer(); 236*a29d6aecSJohannes Altmanninger for (; Offset < Begin; ++Offset) 237*a29d6aecSJohannes Altmanninger printHtml(OS, Code[Offset]); 238*a29d6aecSJohannes Altmanninger OS << "<span id='" << MyTag << Id << "' " 239*a29d6aecSJohannes Altmanninger << "tid='" << OtherTag << TargetId << "' "; 240*a29d6aecSJohannes Altmanninger OS << "title='"; 241*a29d6aecSJohannes Altmanninger printHtml(OS, Node.getTypeLabel()); 242*a29d6aecSJohannes Altmanninger OS << "\n" << LeftId << " -> " << RightId; 243*a29d6aecSJohannes Altmanninger std::string Value = Tree.getNodeValue(Node); 244*a29d6aecSJohannes Altmanninger if (!Value.empty()) { 245*a29d6aecSJohannes Altmanninger OS << "\n"; 246*a29d6aecSJohannes Altmanninger printHtml(OS, Value); 247*a29d6aecSJohannes Altmanninger } 248*a29d6aecSJohannes Altmanninger OS << "'"; 249*a29d6aecSJohannes Altmanninger if (Node.Change != diff::None) 250*a29d6aecSJohannes Altmanninger OS << " class='" << getChangeKindAbbr(Node.Change) << "'"; 251*a29d6aecSJohannes Altmanninger OS << ">"; 252*a29d6aecSJohannes Altmanninger 253*a29d6aecSJohannes Altmanninger for (diff::NodeId Child : Node.Children) 254*a29d6aecSJohannes Altmanninger Offset = printHtmlForNode(OS, Diff, Tree, IsLeft, Child, Offset); 255*a29d6aecSJohannes Altmanninger 256*a29d6aecSJohannes Altmanninger for (; Offset < End; ++Offset) 257*a29d6aecSJohannes Altmanninger printHtml(OS, Code[Offset]); 258*a29d6aecSJohannes Altmanninger if (Id == Tree.getRootId()) { 259*a29d6aecSJohannes Altmanninger End = Code.size(); 260*a29d6aecSJohannes Altmanninger for (; Offset < End; ++Offset) 261*a29d6aecSJohannes Altmanninger printHtml(OS, Code[Offset]); 262*a29d6aecSJohannes Altmanninger } 263*a29d6aecSJohannes Altmanninger OS << "</span>"; 264*a29d6aecSJohannes Altmanninger return Offset; 265*a29d6aecSJohannes Altmanninger } 266*a29d6aecSJohannes Altmanninger 2670da12c84SJohannes Altmanninger static void printJsonString(raw_ostream &OS, const StringRef Str) { 26814be1839SJohannes Altmanninger for (signed char C : Str) { 2690da12c84SJohannes Altmanninger switch (C) { 2700da12c84SJohannes Altmanninger case '"': 2710da12c84SJohannes Altmanninger OS << R"(\")"; 2720da12c84SJohannes Altmanninger break; 2730da12c84SJohannes Altmanninger case '\\': 2740da12c84SJohannes Altmanninger OS << R"(\\)"; 2750da12c84SJohannes Altmanninger break; 2760da12c84SJohannes Altmanninger case '\n': 2770da12c84SJohannes Altmanninger OS << R"(\n)"; 2780da12c84SJohannes Altmanninger break; 2790da12c84SJohannes Altmanninger case '\t': 2800da12c84SJohannes Altmanninger OS << R"(\t)"; 2810da12c84SJohannes Altmanninger break; 2820da12c84SJohannes Altmanninger default: 2830da12c84SJohannes Altmanninger if ('\x00' <= C && C <= '\x1f') { 2840da12c84SJohannes Altmanninger OS << R"(\u00)" << hexdigit(C >> 4) << hexdigit(C); 2850da12c84SJohannes Altmanninger } else { 2860da12c84SJohannes Altmanninger OS << C; 2870da12c84SJohannes Altmanninger } 2880da12c84SJohannes Altmanninger } 2890da12c84SJohannes Altmanninger } 2900da12c84SJohannes Altmanninger } 2910da12c84SJohannes Altmanninger 2920da12c84SJohannes Altmanninger static void printNodeAttributes(raw_ostream &OS, diff::SyntaxTree &Tree, 2930da12c84SJohannes Altmanninger diff::NodeId Id) { 2940da12c84SJohannes Altmanninger const diff::Node &N = Tree.getNode(Id); 2950da12c84SJohannes Altmanninger OS << R"("id":)" << int(Id); 2960da12c84SJohannes Altmanninger OS << R"(,"type":")" << N.getTypeLabel() << '"'; 2970da12c84SJohannes Altmanninger auto Offsets = Tree.getSourceRangeOffsets(N); 2980da12c84SJohannes Altmanninger OS << R"(,"begin":)" << Offsets.first; 2990da12c84SJohannes Altmanninger OS << R"(,"end":)" << Offsets.second; 300e0fe5cd4SJohannes Altmanninger std::string Value = Tree.getNodeValue(N); 3010da12c84SJohannes Altmanninger if (!Value.empty()) { 3020da12c84SJohannes Altmanninger OS << R"(,"value":")"; 3030da12c84SJohannes Altmanninger printJsonString(OS, Value); 3040da12c84SJohannes Altmanninger OS << '"'; 3050da12c84SJohannes Altmanninger } 3060da12c84SJohannes Altmanninger } 3070da12c84SJohannes Altmanninger 3080da12c84SJohannes Altmanninger static void printNodeAsJson(raw_ostream &OS, diff::SyntaxTree &Tree, 3090da12c84SJohannes Altmanninger diff::NodeId Id) { 3100da12c84SJohannes Altmanninger const diff::Node &N = Tree.getNode(Id); 3110da12c84SJohannes Altmanninger OS << "{"; 3120da12c84SJohannes Altmanninger printNodeAttributes(OS, Tree, Id); 3130da12c84SJohannes Altmanninger OS << R"(,"children":[)"; 3140da12c84SJohannes Altmanninger if (N.Children.size() > 0) { 3150da12c84SJohannes Altmanninger printNodeAsJson(OS, Tree, N.Children[0]); 3160da12c84SJohannes Altmanninger for (size_t I = 1, E = N.Children.size(); I < E; ++I) { 3170da12c84SJohannes Altmanninger OS << ","; 3180da12c84SJohannes Altmanninger printNodeAsJson(OS, Tree, N.Children[I]); 3190da12c84SJohannes Altmanninger } 3200da12c84SJohannes Altmanninger } 3210da12c84SJohannes Altmanninger OS << "]}"; 3220da12c84SJohannes Altmanninger } 3230da12c84SJohannes Altmanninger 324e0fe5cd4SJohannes Altmanninger static void printNode(raw_ostream &OS, diff::SyntaxTree &Tree, 325e0fe5cd4SJohannes Altmanninger diff::NodeId Id) { 326e0fe5cd4SJohannes Altmanninger if (Id.isInvalid()) { 327e0fe5cd4SJohannes Altmanninger OS << "None"; 328e0fe5cd4SJohannes Altmanninger return; 329e0fe5cd4SJohannes Altmanninger } 330e0fe5cd4SJohannes Altmanninger OS << Tree.getNode(Id).getTypeLabel(); 331e0fe5cd4SJohannes Altmanninger std::string Value = Tree.getNodeValue(Id); 332e0fe5cd4SJohannes Altmanninger if (!Value.empty()) 333e0fe5cd4SJohannes Altmanninger OS << ": " << Value; 334e0fe5cd4SJohannes Altmanninger OS << "(" << Id << ")"; 335e0fe5cd4SJohannes Altmanninger } 336e0fe5cd4SJohannes Altmanninger 337a1d2b5d5SJohannes Altmanninger static void printTree(raw_ostream &OS, diff::SyntaxTree &Tree) { 338a1d2b5d5SJohannes Altmanninger for (diff::NodeId Id : Tree) { 339a1d2b5d5SJohannes Altmanninger for (int I = 0; I < Tree.getNode(Id).Depth; ++I) 340a1d2b5d5SJohannes Altmanninger OS << " "; 341a1d2b5d5SJohannes Altmanninger printNode(OS, Tree, Id); 342a1d2b5d5SJohannes Altmanninger OS << "\n"; 343a1d2b5d5SJohannes Altmanninger } 344a1d2b5d5SJohannes Altmanninger } 345a1d2b5d5SJohannes Altmanninger 346e0fe5cd4SJohannes Altmanninger static void printDstChange(raw_ostream &OS, diff::ASTDiff &Diff, 347e0fe5cd4SJohannes Altmanninger diff::SyntaxTree &SrcTree, diff::SyntaxTree &DstTree, 348e0fe5cd4SJohannes Altmanninger diff::NodeId Dst) { 349e0fe5cd4SJohannes Altmanninger const diff::Node &DstNode = DstTree.getNode(Dst); 350e0fe5cd4SJohannes Altmanninger diff::NodeId Src = Diff.getMapped(DstTree, Dst); 351e0fe5cd4SJohannes Altmanninger switch (DstNode.Change) { 352e0fe5cd4SJohannes Altmanninger case diff::None: 353e0fe5cd4SJohannes Altmanninger break; 354e0fe5cd4SJohannes Altmanninger case diff::Delete: 355e0fe5cd4SJohannes Altmanninger llvm_unreachable("The destination tree can't have deletions."); 356e0fe5cd4SJohannes Altmanninger case diff::Update: 357e0fe5cd4SJohannes Altmanninger OS << "Update "; 358e0fe5cd4SJohannes Altmanninger printNode(OS, SrcTree, Src); 359e0fe5cd4SJohannes Altmanninger OS << " to " << DstTree.getNodeValue(Dst) << "\n"; 360e0fe5cd4SJohannes Altmanninger break; 361e0fe5cd4SJohannes Altmanninger case diff::Insert: 362e0fe5cd4SJohannes Altmanninger case diff::Move: 363e0fe5cd4SJohannes Altmanninger case diff::UpdateMove: 364e0fe5cd4SJohannes Altmanninger if (DstNode.Change == diff::Insert) 365e0fe5cd4SJohannes Altmanninger OS << "Insert"; 366e0fe5cd4SJohannes Altmanninger else if (DstNode.Change == diff::Move) 367e0fe5cd4SJohannes Altmanninger OS << "Move"; 368e0fe5cd4SJohannes Altmanninger else if (DstNode.Change == diff::UpdateMove) 369e0fe5cd4SJohannes Altmanninger OS << "Update and Move"; 370e0fe5cd4SJohannes Altmanninger OS << " "; 371e0fe5cd4SJohannes Altmanninger printNode(OS, DstTree, Dst); 372e0fe5cd4SJohannes Altmanninger OS << " into "; 373e0fe5cd4SJohannes Altmanninger printNode(OS, DstTree, DstNode.Parent); 374e0fe5cd4SJohannes Altmanninger OS << " at " << DstTree.findPositionInParent(Dst) << "\n"; 375e0fe5cd4SJohannes Altmanninger break; 376e0fe5cd4SJohannes Altmanninger } 377e0fe5cd4SJohannes Altmanninger } 378e0fe5cd4SJohannes Altmanninger 379a75b2cacSAlex Lorenz int main(int argc, const char **argv) { 380849f20e4SJohannes Altmanninger std::string ErrorMessage; 381849f20e4SJohannes Altmanninger std::unique_ptr<CompilationDatabase> CommonCompilations = 382849f20e4SJohannes Altmanninger FixedCompilationDatabase::loadFromCommandLine(argc, argv, ErrorMessage); 383849f20e4SJohannes Altmanninger if (!CommonCompilations && !ErrorMessage.empty()) 384849f20e4SJohannes Altmanninger llvm::errs() << ErrorMessage; 385a75b2cacSAlex Lorenz cl::HideUnrelatedOptions(ClangDiffCategory); 386a75b2cacSAlex Lorenz if (!cl::ParseCommandLineOptions(argc, argv)) { 387a75b2cacSAlex Lorenz cl::PrintOptionValues(); 388a75b2cacSAlex Lorenz return 1; 389a75b2cacSAlex Lorenz } 390a75b2cacSAlex Lorenz 391849f20e4SJohannes Altmanninger addExtraArgs(CommonCompilations); 392849f20e4SJohannes Altmanninger 393a1d2b5d5SJohannes Altmanninger if (ASTDump || ASTDumpJson) { 394a75b2cacSAlex Lorenz if (!DestinationPath.empty()) { 395a75b2cacSAlex Lorenz llvm::errs() << "Error: Please specify exactly one filename.\n"; 396a75b2cacSAlex Lorenz return 1; 397a75b2cacSAlex Lorenz } 398849f20e4SJohannes Altmanninger std::unique_ptr<ASTUnit> AST = getAST(CommonCompilations, SourcePath); 399a75b2cacSAlex Lorenz if (!AST) 400a75b2cacSAlex Lorenz return 1; 401a75b2cacSAlex Lorenz diff::SyntaxTree Tree(AST->getASTContext()); 402a1d2b5d5SJohannes Altmanninger if (ASTDump) { 403a1d2b5d5SJohannes Altmanninger printTree(llvm::outs(), Tree); 404a1d2b5d5SJohannes Altmanninger return 0; 405a1d2b5d5SJohannes Altmanninger } 4060da12c84SJohannes Altmanninger llvm::outs() << R"({"filename":")"; 4070da12c84SJohannes Altmanninger printJsonString(llvm::outs(), SourcePath); 4080da12c84SJohannes Altmanninger llvm::outs() << R"(","root":)"; 4090da12c84SJohannes Altmanninger printNodeAsJson(llvm::outs(), Tree, Tree.getRootId()); 4100da12c84SJohannes Altmanninger llvm::outs() << "}\n"; 411a75b2cacSAlex Lorenz return 0; 412a75b2cacSAlex Lorenz } 413a75b2cacSAlex Lorenz 414a75b2cacSAlex Lorenz if (DestinationPath.empty()) { 415a75b2cacSAlex Lorenz llvm::errs() << "Error: Exactly two paths are required.\n"; 416a75b2cacSAlex Lorenz return 1; 417a75b2cacSAlex Lorenz } 418a75b2cacSAlex Lorenz 419849f20e4SJohannes Altmanninger std::unique_ptr<ASTUnit> Src = getAST(CommonCompilations, SourcePath); 420849f20e4SJohannes Altmanninger std::unique_ptr<ASTUnit> Dst = getAST(CommonCompilations, DestinationPath); 421a75b2cacSAlex Lorenz if (!Src || !Dst) 422a75b2cacSAlex Lorenz return 1; 423a75b2cacSAlex Lorenz 424a75b2cacSAlex Lorenz diff::ComparisonOptions Options; 425849f20e4SJohannes Altmanninger if (MaxSize != -1) 426849f20e4SJohannes Altmanninger Options.MaxSize = MaxSize; 427a75b2cacSAlex Lorenz diff::SyntaxTree SrcTree(Src->getASTContext()); 428a75b2cacSAlex Lorenz diff::SyntaxTree DstTree(Dst->getASTContext()); 429e0fe5cd4SJohannes Altmanninger diff::ASTDiff Diff(SrcTree, DstTree, Options); 430e0fe5cd4SJohannes Altmanninger 431*a29d6aecSJohannes Altmanninger if (HtmlDiff) { 432*a29d6aecSJohannes Altmanninger llvm::outs() << HtmlDiffHeader << "<pre>"; 433*a29d6aecSJohannes Altmanninger llvm::outs() << "<div id='L' class='code'>"; 434*a29d6aecSJohannes Altmanninger printHtmlForNode(llvm::outs(), Diff, SrcTree, true, SrcTree.getRootId(), 0); 435*a29d6aecSJohannes Altmanninger llvm::outs() << "</div>"; 436*a29d6aecSJohannes Altmanninger llvm::outs() << "<div id='R' class='code'>"; 437*a29d6aecSJohannes Altmanninger printHtmlForNode(llvm::outs(), Diff, DstTree, false, DstTree.getRootId(), 438*a29d6aecSJohannes Altmanninger 0); 439*a29d6aecSJohannes Altmanninger llvm::outs() << "</div>"; 440*a29d6aecSJohannes Altmanninger llvm::outs() << "</pre></div></body></html>\n"; 441*a29d6aecSJohannes Altmanninger return 0; 442*a29d6aecSJohannes Altmanninger } 443*a29d6aecSJohannes Altmanninger 444e0fe5cd4SJohannes Altmanninger for (diff::NodeId Dst : DstTree) { 445e0fe5cd4SJohannes Altmanninger diff::NodeId Src = Diff.getMapped(DstTree, Dst); 446683876caSJohannes Altmanninger if (PrintMatches && Src.isValid()) { 447e0fe5cd4SJohannes Altmanninger llvm::outs() << "Match "; 448e0fe5cd4SJohannes Altmanninger printNode(llvm::outs(), SrcTree, Src); 449e0fe5cd4SJohannes Altmanninger llvm::outs() << " to "; 450e0fe5cd4SJohannes Altmanninger printNode(llvm::outs(), DstTree, Dst); 451e0fe5cd4SJohannes Altmanninger llvm::outs() << "\n"; 452e0fe5cd4SJohannes Altmanninger } 453e0fe5cd4SJohannes Altmanninger printDstChange(llvm::outs(), Diff, SrcTree, DstTree, Dst); 454e0fe5cd4SJohannes Altmanninger } 455e0fe5cd4SJohannes Altmanninger for (diff::NodeId Src : SrcTree) { 456e0fe5cd4SJohannes Altmanninger if (Diff.getMapped(SrcTree, Src).isInvalid()) { 457e0fe5cd4SJohannes Altmanninger llvm::outs() << "Delete "; 458e0fe5cd4SJohannes Altmanninger printNode(llvm::outs(), SrcTree, Src); 459e0fe5cd4SJohannes Altmanninger llvm::outs() << "\n"; 460e0fe5cd4SJohannes Altmanninger } 461e0fe5cd4SJohannes Altmanninger } 462a75b2cacSAlex Lorenz 463a75b2cacSAlex Lorenz return 0; 464a75b2cacSAlex Lorenz } 465