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