1*e5dd7070Spatrick //===- ClangDiff.cpp - compare source files by AST nodes ------*- C++ -*- -===// 2*e5dd7070Spatrick // 3*e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information. 5*e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*e5dd7070Spatrick // 7*e5dd7070Spatrick //===----------------------------------------------------------------------===// 8*e5dd7070Spatrick // 9*e5dd7070Spatrick // This file implements a tool for syntax tree based comparison using 10*e5dd7070Spatrick // Tooling/ASTDiff. 11*e5dd7070Spatrick // 12*e5dd7070Spatrick //===----------------------------------------------------------------------===// 13*e5dd7070Spatrick 14*e5dd7070Spatrick #include "clang/Tooling/ASTDiff/ASTDiff.h" 15*e5dd7070Spatrick #include "clang/Tooling/CommonOptionsParser.h" 16*e5dd7070Spatrick #include "clang/Tooling/Tooling.h" 17*e5dd7070Spatrick #include "llvm/Support/CommandLine.h" 18*e5dd7070Spatrick 19*e5dd7070Spatrick using namespace llvm; 20*e5dd7070Spatrick using namespace clang; 21*e5dd7070Spatrick using namespace clang::tooling; 22*e5dd7070Spatrick 23*e5dd7070Spatrick static cl::OptionCategory ClangDiffCategory("clang-diff options"); 24*e5dd7070Spatrick 25*e5dd7070Spatrick static cl::opt<bool> 26*e5dd7070Spatrick ASTDump("ast-dump", 27*e5dd7070Spatrick cl::desc("Print the internal representation of the AST."), 28*e5dd7070Spatrick cl::init(false), cl::cat(ClangDiffCategory)); 29*e5dd7070Spatrick 30*e5dd7070Spatrick static cl::opt<bool> ASTDumpJson( 31*e5dd7070Spatrick "ast-dump-json", 32*e5dd7070Spatrick cl::desc("Print the internal representation of the AST as JSON."), 33*e5dd7070Spatrick cl::init(false), cl::cat(ClangDiffCategory)); 34*e5dd7070Spatrick 35*e5dd7070Spatrick static cl::opt<bool> PrintMatches("dump-matches", 36*e5dd7070Spatrick cl::desc("Print the matched nodes."), 37*e5dd7070Spatrick cl::init(false), cl::cat(ClangDiffCategory)); 38*e5dd7070Spatrick 39*e5dd7070Spatrick static cl::opt<bool> HtmlDiff("html", 40*e5dd7070Spatrick cl::desc("Output a side-by-side diff in HTML."), 41*e5dd7070Spatrick cl::init(false), cl::cat(ClangDiffCategory)); 42*e5dd7070Spatrick 43*e5dd7070Spatrick static cl::opt<std::string> SourcePath(cl::Positional, cl::desc("<source>"), 44*e5dd7070Spatrick cl::Required, 45*e5dd7070Spatrick cl::cat(ClangDiffCategory)); 46*e5dd7070Spatrick 47*e5dd7070Spatrick static cl::opt<std::string> DestinationPath(cl::Positional, 48*e5dd7070Spatrick cl::desc("<destination>"), 49*e5dd7070Spatrick cl::Optional, 50*e5dd7070Spatrick cl::cat(ClangDiffCategory)); 51*e5dd7070Spatrick 52*e5dd7070Spatrick static cl::opt<std::string> StopAfter("stop-diff-after", 53*e5dd7070Spatrick cl::desc("<topdown|bottomup>"), 54*e5dd7070Spatrick cl::Optional, cl::init(""), 55*e5dd7070Spatrick cl::cat(ClangDiffCategory)); 56*e5dd7070Spatrick 57*e5dd7070Spatrick static cl::opt<int> MaxSize("s", cl::desc("<maxsize>"), cl::Optional, 58*e5dd7070Spatrick cl::init(-1), cl::cat(ClangDiffCategory)); 59*e5dd7070Spatrick 60*e5dd7070Spatrick static cl::opt<std::string> BuildPath("p", cl::desc("Build path"), cl::init(""), 61*e5dd7070Spatrick cl::Optional, cl::cat(ClangDiffCategory)); 62*e5dd7070Spatrick 63*e5dd7070Spatrick static cl::list<std::string> ArgsAfter( 64*e5dd7070Spatrick "extra-arg", 65*e5dd7070Spatrick cl::desc("Additional argument to append to the compiler command line"), 66*e5dd7070Spatrick cl::cat(ClangDiffCategory)); 67*e5dd7070Spatrick 68*e5dd7070Spatrick static cl::list<std::string> ArgsBefore( 69*e5dd7070Spatrick "extra-arg-before", 70*e5dd7070Spatrick cl::desc("Additional argument to prepend to the compiler command line"), 71*e5dd7070Spatrick cl::cat(ClangDiffCategory)); 72*e5dd7070Spatrick 73*e5dd7070Spatrick static void addExtraArgs(std::unique_ptr<CompilationDatabase> &Compilations) { 74*e5dd7070Spatrick if (!Compilations) 75*e5dd7070Spatrick return; 76*e5dd7070Spatrick auto AdjustingCompilations = 77*e5dd7070Spatrick std::make_unique<ArgumentsAdjustingCompilations>( 78*e5dd7070Spatrick std::move(Compilations)); 79*e5dd7070Spatrick AdjustingCompilations->appendArgumentsAdjuster( 80*e5dd7070Spatrick getInsertArgumentAdjuster(ArgsBefore, ArgumentInsertPosition::BEGIN)); 81*e5dd7070Spatrick AdjustingCompilations->appendArgumentsAdjuster( 82*e5dd7070Spatrick getInsertArgumentAdjuster(ArgsAfter, ArgumentInsertPosition::END)); 83*e5dd7070Spatrick Compilations = std::move(AdjustingCompilations); 84*e5dd7070Spatrick } 85*e5dd7070Spatrick 86*e5dd7070Spatrick static std::unique_ptr<ASTUnit> 87*e5dd7070Spatrick getAST(const std::unique_ptr<CompilationDatabase> &CommonCompilations, 88*e5dd7070Spatrick const StringRef Filename) { 89*e5dd7070Spatrick std::string ErrorMessage; 90*e5dd7070Spatrick std::unique_ptr<CompilationDatabase> Compilations; 91*e5dd7070Spatrick if (!CommonCompilations) { 92*e5dd7070Spatrick Compilations = CompilationDatabase::autoDetectFromSource( 93*e5dd7070Spatrick BuildPath.empty() ? Filename : BuildPath, ErrorMessage); 94*e5dd7070Spatrick if (!Compilations) { 95*e5dd7070Spatrick llvm::errs() 96*e5dd7070Spatrick << "Error while trying to load a compilation database, running " 97*e5dd7070Spatrick "without flags.\n" 98*e5dd7070Spatrick << ErrorMessage; 99*e5dd7070Spatrick Compilations = 100*e5dd7070Spatrick std::make_unique<clang::tooling::FixedCompilationDatabase>( 101*e5dd7070Spatrick ".", std::vector<std::string>()); 102*e5dd7070Spatrick } 103*e5dd7070Spatrick } 104*e5dd7070Spatrick addExtraArgs(Compilations); 105*e5dd7070Spatrick std::array<std::string, 1> Files = {{Filename}}; 106*e5dd7070Spatrick ClangTool Tool(Compilations ? *Compilations : *CommonCompilations, Files); 107*e5dd7070Spatrick std::vector<std::unique_ptr<ASTUnit>> ASTs; 108*e5dd7070Spatrick Tool.buildASTs(ASTs); 109*e5dd7070Spatrick if (ASTs.size() != Files.size()) 110*e5dd7070Spatrick return nullptr; 111*e5dd7070Spatrick return std::move(ASTs[0]); 112*e5dd7070Spatrick } 113*e5dd7070Spatrick 114*e5dd7070Spatrick static char hexdigit(int N) { return N &= 0xf, N + (N < 10 ? '0' : 'a' - 10); } 115*e5dd7070Spatrick 116*e5dd7070Spatrick static const char HtmlDiffHeader[] = R"( 117*e5dd7070Spatrick <html> 118*e5dd7070Spatrick <head> 119*e5dd7070Spatrick <meta charset='utf-8'/> 120*e5dd7070Spatrick <style> 121*e5dd7070Spatrick span.d { color: red; } 122*e5dd7070Spatrick span.u { color: #cc00cc; } 123*e5dd7070Spatrick span.i { color: green; } 124*e5dd7070Spatrick span.m { font-weight: bold; } 125*e5dd7070Spatrick span { font-weight: normal; color: black; } 126*e5dd7070Spatrick div.code { 127*e5dd7070Spatrick width: 48%; 128*e5dd7070Spatrick height: 98%; 129*e5dd7070Spatrick overflow: scroll; 130*e5dd7070Spatrick float: left; 131*e5dd7070Spatrick padding: 0 0 0.5% 0.5%; 132*e5dd7070Spatrick border: solid 2px LightGrey; 133*e5dd7070Spatrick border-radius: 5px; 134*e5dd7070Spatrick } 135*e5dd7070Spatrick </style> 136*e5dd7070Spatrick </head> 137*e5dd7070Spatrick <script type='text/javascript'> 138*e5dd7070Spatrick highlightStack = [] 139*e5dd7070Spatrick function clearHighlight() { 140*e5dd7070Spatrick while (highlightStack.length) { 141*e5dd7070Spatrick var [l, r] = highlightStack.pop() 142*e5dd7070Spatrick document.getElementById(l).style.backgroundColor = 'inherit' 143*e5dd7070Spatrick if (r[1] != '-') 144*e5dd7070Spatrick document.getElementById(r).style.backgroundColor = 'inherit' 145*e5dd7070Spatrick } 146*e5dd7070Spatrick } 147*e5dd7070Spatrick function highlight(event) { 148*e5dd7070Spatrick var id = event.target['id'] 149*e5dd7070Spatrick doHighlight(id) 150*e5dd7070Spatrick } 151*e5dd7070Spatrick function doHighlight(id) { 152*e5dd7070Spatrick clearHighlight() 153*e5dd7070Spatrick source = document.getElementById(id) 154*e5dd7070Spatrick if (!source.attributes['tid']) 155*e5dd7070Spatrick return 156*e5dd7070Spatrick var mapped = source 157*e5dd7070Spatrick while (mapped && mapped.parentElement && mapped.attributes['tid'].value.substr(1) === '-1') 158*e5dd7070Spatrick mapped = mapped.parentElement 159*e5dd7070Spatrick var tid = null, target = null 160*e5dd7070Spatrick if (mapped) { 161*e5dd7070Spatrick tid = mapped.attributes['tid'].value 162*e5dd7070Spatrick target = document.getElementById(tid) 163*e5dd7070Spatrick } 164*e5dd7070Spatrick if (source.parentElement && source.parentElement.classList.contains('code')) 165*e5dd7070Spatrick return 166*e5dd7070Spatrick source.style.backgroundColor = 'lightgrey' 167*e5dd7070Spatrick source.scrollIntoView() 168*e5dd7070Spatrick if (target) { 169*e5dd7070Spatrick if (mapped === source) 170*e5dd7070Spatrick target.style.backgroundColor = 'lightgrey' 171*e5dd7070Spatrick target.scrollIntoView() 172*e5dd7070Spatrick } 173*e5dd7070Spatrick highlightStack.push([id, tid]) 174*e5dd7070Spatrick location.hash = '#' + id 175*e5dd7070Spatrick } 176*e5dd7070Spatrick function scrollToBoth() { 177*e5dd7070Spatrick doHighlight(location.hash.substr(1)) 178*e5dd7070Spatrick } 179*e5dd7070Spatrick function changed(elem) { 180*e5dd7070Spatrick return elem.classList.length == 0 181*e5dd7070Spatrick } 182*e5dd7070Spatrick function nextChangedNode(prefix, increment, number) { 183*e5dd7070Spatrick do { 184*e5dd7070Spatrick number += increment 185*e5dd7070Spatrick var elem = document.getElementById(prefix + number) 186*e5dd7070Spatrick } while(elem && !changed(elem)) 187*e5dd7070Spatrick return elem ? number : null 188*e5dd7070Spatrick } 189*e5dd7070Spatrick function handleKey(e) { 190*e5dd7070Spatrick var down = e.code === "KeyJ" 191*e5dd7070Spatrick var up = e.code === "KeyK" 192*e5dd7070Spatrick if (!down && !up) 193*e5dd7070Spatrick return 194*e5dd7070Spatrick var id = highlightStack[0] ? highlightStack[0][0] : 'R0' 195*e5dd7070Spatrick var oldelem = document.getElementById(id) 196*e5dd7070Spatrick var number = parseInt(id.substr(1)) 197*e5dd7070Spatrick var increment = down ? 1 : -1 198*e5dd7070Spatrick var lastnumber = number 199*e5dd7070Spatrick var prefix = id[0] 200*e5dd7070Spatrick do { 201*e5dd7070Spatrick number = nextChangedNode(prefix, increment, number) 202*e5dd7070Spatrick var elem = document.getElementById(prefix + number) 203*e5dd7070Spatrick if (up && elem) { 204*e5dd7070Spatrick while (elem.parentElement && changed(elem.parentElement)) 205*e5dd7070Spatrick elem = elem.parentElement 206*e5dd7070Spatrick number = elem.id.substr(1) 207*e5dd7070Spatrick } 208*e5dd7070Spatrick } while ((down && id !== 'R0' && oldelem.contains(elem))) 209*e5dd7070Spatrick if (!number) 210*e5dd7070Spatrick number = lastnumber 211*e5dd7070Spatrick elem = document.getElementById(prefix + number) 212*e5dd7070Spatrick doHighlight(prefix + number) 213*e5dd7070Spatrick } 214*e5dd7070Spatrick window.onload = scrollToBoth 215*e5dd7070Spatrick window.onkeydown = handleKey 216*e5dd7070Spatrick </script> 217*e5dd7070Spatrick <body> 218*e5dd7070Spatrick <div onclick='highlight(event)'> 219*e5dd7070Spatrick )"; 220*e5dd7070Spatrick 221*e5dd7070Spatrick static void printHtml(raw_ostream &OS, char C) { 222*e5dd7070Spatrick switch (C) { 223*e5dd7070Spatrick case '&': 224*e5dd7070Spatrick OS << "&"; 225*e5dd7070Spatrick break; 226*e5dd7070Spatrick case '<': 227*e5dd7070Spatrick OS << "<"; 228*e5dd7070Spatrick break; 229*e5dd7070Spatrick case '>': 230*e5dd7070Spatrick OS << ">"; 231*e5dd7070Spatrick break; 232*e5dd7070Spatrick case '\'': 233*e5dd7070Spatrick OS << "'"; 234*e5dd7070Spatrick break; 235*e5dd7070Spatrick case '"': 236*e5dd7070Spatrick OS << """; 237*e5dd7070Spatrick break; 238*e5dd7070Spatrick default: 239*e5dd7070Spatrick OS << C; 240*e5dd7070Spatrick } 241*e5dd7070Spatrick } 242*e5dd7070Spatrick 243*e5dd7070Spatrick static void printHtml(raw_ostream &OS, const StringRef Str) { 244*e5dd7070Spatrick for (char C : Str) 245*e5dd7070Spatrick printHtml(OS, C); 246*e5dd7070Spatrick } 247*e5dd7070Spatrick 248*e5dd7070Spatrick static std::string getChangeKindAbbr(diff::ChangeKind Kind) { 249*e5dd7070Spatrick switch (Kind) { 250*e5dd7070Spatrick case diff::None: 251*e5dd7070Spatrick return ""; 252*e5dd7070Spatrick case diff::Delete: 253*e5dd7070Spatrick return "d"; 254*e5dd7070Spatrick case diff::Update: 255*e5dd7070Spatrick return "u"; 256*e5dd7070Spatrick case diff::Insert: 257*e5dd7070Spatrick return "i"; 258*e5dd7070Spatrick case diff::Move: 259*e5dd7070Spatrick return "m"; 260*e5dd7070Spatrick case diff::UpdateMove: 261*e5dd7070Spatrick return "u m"; 262*e5dd7070Spatrick } 263*e5dd7070Spatrick llvm_unreachable("Invalid enumeration value."); 264*e5dd7070Spatrick } 265*e5dd7070Spatrick 266*e5dd7070Spatrick static unsigned printHtmlForNode(raw_ostream &OS, const diff::ASTDiff &Diff, 267*e5dd7070Spatrick diff::SyntaxTree &Tree, bool IsLeft, 268*e5dd7070Spatrick diff::NodeId Id, unsigned Offset) { 269*e5dd7070Spatrick const diff::Node &Node = Tree.getNode(Id); 270*e5dd7070Spatrick char MyTag, OtherTag; 271*e5dd7070Spatrick diff::NodeId LeftId, RightId; 272*e5dd7070Spatrick diff::NodeId TargetId = Diff.getMapped(Tree, Id); 273*e5dd7070Spatrick if (IsLeft) { 274*e5dd7070Spatrick MyTag = 'L'; 275*e5dd7070Spatrick OtherTag = 'R'; 276*e5dd7070Spatrick LeftId = Id; 277*e5dd7070Spatrick RightId = TargetId; 278*e5dd7070Spatrick } else { 279*e5dd7070Spatrick MyTag = 'R'; 280*e5dd7070Spatrick OtherTag = 'L'; 281*e5dd7070Spatrick LeftId = TargetId; 282*e5dd7070Spatrick RightId = Id; 283*e5dd7070Spatrick } 284*e5dd7070Spatrick unsigned Begin, End; 285*e5dd7070Spatrick std::tie(Begin, End) = Tree.getSourceRangeOffsets(Node); 286*e5dd7070Spatrick const SourceManager &SrcMgr = Tree.getASTContext().getSourceManager(); 287*e5dd7070Spatrick auto Code = SrcMgr.getBuffer(SrcMgr.getMainFileID())->getBuffer(); 288*e5dd7070Spatrick for (; Offset < Begin; ++Offset) 289*e5dd7070Spatrick printHtml(OS, Code[Offset]); 290*e5dd7070Spatrick OS << "<span id='" << MyTag << Id << "' " 291*e5dd7070Spatrick << "tid='" << OtherTag << TargetId << "' "; 292*e5dd7070Spatrick OS << "title='"; 293*e5dd7070Spatrick printHtml(OS, Node.getTypeLabel()); 294*e5dd7070Spatrick OS << "\n" << LeftId << " -> " << RightId; 295*e5dd7070Spatrick std::string Value = Tree.getNodeValue(Node); 296*e5dd7070Spatrick if (!Value.empty()) { 297*e5dd7070Spatrick OS << "\n"; 298*e5dd7070Spatrick printHtml(OS, Value); 299*e5dd7070Spatrick } 300*e5dd7070Spatrick OS << "'"; 301*e5dd7070Spatrick if (Node.Change != diff::None) 302*e5dd7070Spatrick OS << " class='" << getChangeKindAbbr(Node.Change) << "'"; 303*e5dd7070Spatrick OS << ">"; 304*e5dd7070Spatrick 305*e5dd7070Spatrick for (diff::NodeId Child : Node.Children) 306*e5dd7070Spatrick Offset = printHtmlForNode(OS, Diff, Tree, IsLeft, Child, Offset); 307*e5dd7070Spatrick 308*e5dd7070Spatrick for (; Offset < End; ++Offset) 309*e5dd7070Spatrick printHtml(OS, Code[Offset]); 310*e5dd7070Spatrick if (Id == Tree.getRootId()) { 311*e5dd7070Spatrick End = Code.size(); 312*e5dd7070Spatrick for (; Offset < End; ++Offset) 313*e5dd7070Spatrick printHtml(OS, Code[Offset]); 314*e5dd7070Spatrick } 315*e5dd7070Spatrick OS << "</span>"; 316*e5dd7070Spatrick return Offset; 317*e5dd7070Spatrick } 318*e5dd7070Spatrick 319*e5dd7070Spatrick static void printJsonString(raw_ostream &OS, const StringRef Str) { 320*e5dd7070Spatrick for (signed char C : Str) { 321*e5dd7070Spatrick switch (C) { 322*e5dd7070Spatrick case '"': 323*e5dd7070Spatrick OS << R"(\")"; 324*e5dd7070Spatrick break; 325*e5dd7070Spatrick case '\\': 326*e5dd7070Spatrick OS << R"(\\)"; 327*e5dd7070Spatrick break; 328*e5dd7070Spatrick case '\n': 329*e5dd7070Spatrick OS << R"(\n)"; 330*e5dd7070Spatrick break; 331*e5dd7070Spatrick case '\t': 332*e5dd7070Spatrick OS << R"(\t)"; 333*e5dd7070Spatrick break; 334*e5dd7070Spatrick default: 335*e5dd7070Spatrick if ('\x00' <= C && C <= '\x1f') { 336*e5dd7070Spatrick OS << R"(\u00)" << hexdigit(C >> 4) << hexdigit(C); 337*e5dd7070Spatrick } else { 338*e5dd7070Spatrick OS << C; 339*e5dd7070Spatrick } 340*e5dd7070Spatrick } 341*e5dd7070Spatrick } 342*e5dd7070Spatrick } 343*e5dd7070Spatrick 344*e5dd7070Spatrick static void printNodeAttributes(raw_ostream &OS, diff::SyntaxTree &Tree, 345*e5dd7070Spatrick diff::NodeId Id) { 346*e5dd7070Spatrick const diff::Node &N = Tree.getNode(Id); 347*e5dd7070Spatrick OS << R"("id":)" << int(Id); 348*e5dd7070Spatrick OS << R"(,"type":")" << N.getTypeLabel() << '"'; 349*e5dd7070Spatrick auto Offsets = Tree.getSourceRangeOffsets(N); 350*e5dd7070Spatrick OS << R"(,"begin":)" << Offsets.first; 351*e5dd7070Spatrick OS << R"(,"end":)" << Offsets.second; 352*e5dd7070Spatrick std::string Value = Tree.getNodeValue(N); 353*e5dd7070Spatrick if (!Value.empty()) { 354*e5dd7070Spatrick OS << R"(,"value":")"; 355*e5dd7070Spatrick printJsonString(OS, Value); 356*e5dd7070Spatrick OS << '"'; 357*e5dd7070Spatrick } 358*e5dd7070Spatrick } 359*e5dd7070Spatrick 360*e5dd7070Spatrick static void printNodeAsJson(raw_ostream &OS, diff::SyntaxTree &Tree, 361*e5dd7070Spatrick diff::NodeId Id) { 362*e5dd7070Spatrick const diff::Node &N = Tree.getNode(Id); 363*e5dd7070Spatrick OS << "{"; 364*e5dd7070Spatrick printNodeAttributes(OS, Tree, Id); 365*e5dd7070Spatrick auto Identifier = N.getIdentifier(); 366*e5dd7070Spatrick auto QualifiedIdentifier = N.getQualifiedIdentifier(); 367*e5dd7070Spatrick if (Identifier) { 368*e5dd7070Spatrick OS << R"(,"identifier":")"; 369*e5dd7070Spatrick printJsonString(OS, *Identifier); 370*e5dd7070Spatrick OS << R"(")"; 371*e5dd7070Spatrick if (QualifiedIdentifier && *Identifier != *QualifiedIdentifier) { 372*e5dd7070Spatrick OS << R"(,"qualified_identifier":")"; 373*e5dd7070Spatrick printJsonString(OS, *QualifiedIdentifier); 374*e5dd7070Spatrick OS << R"(")"; 375*e5dd7070Spatrick } 376*e5dd7070Spatrick } 377*e5dd7070Spatrick OS << R"(,"children":[)"; 378*e5dd7070Spatrick if (N.Children.size() > 0) { 379*e5dd7070Spatrick printNodeAsJson(OS, Tree, N.Children[0]); 380*e5dd7070Spatrick for (size_t I = 1, E = N.Children.size(); I < E; ++I) { 381*e5dd7070Spatrick OS << ","; 382*e5dd7070Spatrick printNodeAsJson(OS, Tree, N.Children[I]); 383*e5dd7070Spatrick } 384*e5dd7070Spatrick } 385*e5dd7070Spatrick OS << "]}"; 386*e5dd7070Spatrick } 387*e5dd7070Spatrick 388*e5dd7070Spatrick static void printNode(raw_ostream &OS, diff::SyntaxTree &Tree, 389*e5dd7070Spatrick diff::NodeId Id) { 390*e5dd7070Spatrick if (Id.isInvalid()) { 391*e5dd7070Spatrick OS << "None"; 392*e5dd7070Spatrick return; 393*e5dd7070Spatrick } 394*e5dd7070Spatrick OS << Tree.getNode(Id).getTypeLabel(); 395*e5dd7070Spatrick std::string Value = Tree.getNodeValue(Id); 396*e5dd7070Spatrick if (!Value.empty()) 397*e5dd7070Spatrick OS << ": " << Value; 398*e5dd7070Spatrick OS << "(" << Id << ")"; 399*e5dd7070Spatrick } 400*e5dd7070Spatrick 401*e5dd7070Spatrick static void printTree(raw_ostream &OS, diff::SyntaxTree &Tree) { 402*e5dd7070Spatrick for (diff::NodeId Id : Tree) { 403*e5dd7070Spatrick for (int I = 0; I < Tree.getNode(Id).Depth; ++I) 404*e5dd7070Spatrick OS << " "; 405*e5dd7070Spatrick printNode(OS, Tree, Id); 406*e5dd7070Spatrick OS << "\n"; 407*e5dd7070Spatrick } 408*e5dd7070Spatrick } 409*e5dd7070Spatrick 410*e5dd7070Spatrick static void printDstChange(raw_ostream &OS, diff::ASTDiff &Diff, 411*e5dd7070Spatrick diff::SyntaxTree &SrcTree, diff::SyntaxTree &DstTree, 412*e5dd7070Spatrick diff::NodeId Dst) { 413*e5dd7070Spatrick const diff::Node &DstNode = DstTree.getNode(Dst); 414*e5dd7070Spatrick diff::NodeId Src = Diff.getMapped(DstTree, Dst); 415*e5dd7070Spatrick switch (DstNode.Change) { 416*e5dd7070Spatrick case diff::None: 417*e5dd7070Spatrick break; 418*e5dd7070Spatrick case diff::Delete: 419*e5dd7070Spatrick llvm_unreachable("The destination tree can't have deletions."); 420*e5dd7070Spatrick case diff::Update: 421*e5dd7070Spatrick OS << "Update "; 422*e5dd7070Spatrick printNode(OS, SrcTree, Src); 423*e5dd7070Spatrick OS << " to " << DstTree.getNodeValue(Dst) << "\n"; 424*e5dd7070Spatrick break; 425*e5dd7070Spatrick case diff::Insert: 426*e5dd7070Spatrick case diff::Move: 427*e5dd7070Spatrick case diff::UpdateMove: 428*e5dd7070Spatrick if (DstNode.Change == diff::Insert) 429*e5dd7070Spatrick OS << "Insert"; 430*e5dd7070Spatrick else if (DstNode.Change == diff::Move) 431*e5dd7070Spatrick OS << "Move"; 432*e5dd7070Spatrick else if (DstNode.Change == diff::UpdateMove) 433*e5dd7070Spatrick OS << "Update and Move"; 434*e5dd7070Spatrick OS << " "; 435*e5dd7070Spatrick printNode(OS, DstTree, Dst); 436*e5dd7070Spatrick OS << " into "; 437*e5dd7070Spatrick printNode(OS, DstTree, DstNode.Parent); 438*e5dd7070Spatrick OS << " at " << DstTree.findPositionInParent(Dst) << "\n"; 439*e5dd7070Spatrick break; 440*e5dd7070Spatrick } 441*e5dd7070Spatrick } 442*e5dd7070Spatrick 443*e5dd7070Spatrick int main(int argc, const char **argv) { 444*e5dd7070Spatrick std::string ErrorMessage; 445*e5dd7070Spatrick std::unique_ptr<CompilationDatabase> CommonCompilations = 446*e5dd7070Spatrick FixedCompilationDatabase::loadFromCommandLine(argc, argv, ErrorMessage); 447*e5dd7070Spatrick if (!CommonCompilations && !ErrorMessage.empty()) 448*e5dd7070Spatrick llvm::errs() << ErrorMessage; 449*e5dd7070Spatrick cl::HideUnrelatedOptions(ClangDiffCategory); 450*e5dd7070Spatrick if (!cl::ParseCommandLineOptions(argc, argv)) { 451*e5dd7070Spatrick cl::PrintOptionValues(); 452*e5dd7070Spatrick return 1; 453*e5dd7070Spatrick } 454*e5dd7070Spatrick 455*e5dd7070Spatrick addExtraArgs(CommonCompilations); 456*e5dd7070Spatrick 457*e5dd7070Spatrick if (ASTDump || ASTDumpJson) { 458*e5dd7070Spatrick if (!DestinationPath.empty()) { 459*e5dd7070Spatrick llvm::errs() << "Error: Please specify exactly one filename.\n"; 460*e5dd7070Spatrick return 1; 461*e5dd7070Spatrick } 462*e5dd7070Spatrick std::unique_ptr<ASTUnit> AST = getAST(CommonCompilations, SourcePath); 463*e5dd7070Spatrick if (!AST) 464*e5dd7070Spatrick return 1; 465*e5dd7070Spatrick diff::SyntaxTree Tree(AST->getASTContext()); 466*e5dd7070Spatrick if (ASTDump) { 467*e5dd7070Spatrick printTree(llvm::outs(), Tree); 468*e5dd7070Spatrick return 0; 469*e5dd7070Spatrick } 470*e5dd7070Spatrick llvm::outs() << R"({"filename":")"; 471*e5dd7070Spatrick printJsonString(llvm::outs(), SourcePath); 472*e5dd7070Spatrick llvm::outs() << R"(","root":)"; 473*e5dd7070Spatrick printNodeAsJson(llvm::outs(), Tree, Tree.getRootId()); 474*e5dd7070Spatrick llvm::outs() << "}\n"; 475*e5dd7070Spatrick return 0; 476*e5dd7070Spatrick } 477*e5dd7070Spatrick 478*e5dd7070Spatrick if (DestinationPath.empty()) { 479*e5dd7070Spatrick llvm::errs() << "Error: Exactly two paths are required.\n"; 480*e5dd7070Spatrick return 1; 481*e5dd7070Spatrick } 482*e5dd7070Spatrick 483*e5dd7070Spatrick std::unique_ptr<ASTUnit> Src = getAST(CommonCompilations, SourcePath); 484*e5dd7070Spatrick std::unique_ptr<ASTUnit> Dst = getAST(CommonCompilations, DestinationPath); 485*e5dd7070Spatrick if (!Src || !Dst) 486*e5dd7070Spatrick return 1; 487*e5dd7070Spatrick 488*e5dd7070Spatrick diff::ComparisonOptions Options; 489*e5dd7070Spatrick if (MaxSize != -1) 490*e5dd7070Spatrick Options.MaxSize = MaxSize; 491*e5dd7070Spatrick if (!StopAfter.empty()) { 492*e5dd7070Spatrick if (StopAfter == "topdown") 493*e5dd7070Spatrick Options.StopAfterTopDown = true; 494*e5dd7070Spatrick else if (StopAfter != "bottomup") { 495*e5dd7070Spatrick llvm::errs() << "Error: Invalid argument for -stop-after\n"; 496*e5dd7070Spatrick return 1; 497*e5dd7070Spatrick } 498*e5dd7070Spatrick } 499*e5dd7070Spatrick diff::SyntaxTree SrcTree(Src->getASTContext()); 500*e5dd7070Spatrick diff::SyntaxTree DstTree(Dst->getASTContext()); 501*e5dd7070Spatrick diff::ASTDiff Diff(SrcTree, DstTree, Options); 502*e5dd7070Spatrick 503*e5dd7070Spatrick if (HtmlDiff) { 504*e5dd7070Spatrick llvm::outs() << HtmlDiffHeader << "<pre>"; 505*e5dd7070Spatrick llvm::outs() << "<div id='L' class='code'>"; 506*e5dd7070Spatrick printHtmlForNode(llvm::outs(), Diff, SrcTree, true, SrcTree.getRootId(), 0); 507*e5dd7070Spatrick llvm::outs() << "</div>"; 508*e5dd7070Spatrick llvm::outs() << "<div id='R' class='code'>"; 509*e5dd7070Spatrick printHtmlForNode(llvm::outs(), Diff, DstTree, false, DstTree.getRootId(), 510*e5dd7070Spatrick 0); 511*e5dd7070Spatrick llvm::outs() << "</div>"; 512*e5dd7070Spatrick llvm::outs() << "</pre></div></body></html>\n"; 513*e5dd7070Spatrick return 0; 514*e5dd7070Spatrick } 515*e5dd7070Spatrick 516*e5dd7070Spatrick for (diff::NodeId Dst : DstTree) { 517*e5dd7070Spatrick diff::NodeId Src = Diff.getMapped(DstTree, Dst); 518*e5dd7070Spatrick if (PrintMatches && Src.isValid()) { 519*e5dd7070Spatrick llvm::outs() << "Match "; 520*e5dd7070Spatrick printNode(llvm::outs(), SrcTree, Src); 521*e5dd7070Spatrick llvm::outs() << " to "; 522*e5dd7070Spatrick printNode(llvm::outs(), DstTree, Dst); 523*e5dd7070Spatrick llvm::outs() << "\n"; 524*e5dd7070Spatrick } 525*e5dd7070Spatrick printDstChange(llvm::outs(), Diff, SrcTree, DstTree, Dst); 526*e5dd7070Spatrick } 527*e5dd7070Spatrick for (diff::NodeId Src : SrcTree) { 528*e5dd7070Spatrick if (Diff.getMapped(SrcTree, Src).isInvalid()) { 529*e5dd7070Spatrick llvm::outs() << "Delete "; 530*e5dd7070Spatrick printNode(llvm::outs(), SrcTree, Src); 531*e5dd7070Spatrick llvm::outs() << "\n"; 532*e5dd7070Spatrick } 533*e5dd7070Spatrick } 534*e5dd7070Spatrick 535*e5dd7070Spatrick return 0; 536*e5dd7070Spatrick } 537