xref: /netbsd-src/external/apache2/llvm/dist/clang/tools/clang-diff/ClangDiff.cpp (revision e038c9c4676b0f19b1b7dd08a940c6ed64a6d5ae)
17330f729Sjoerg //===- ClangDiff.cpp - compare source files by AST nodes ------*- C++ -*- -===//
27330f729Sjoerg //
37330f729Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
47330f729Sjoerg // See https://llvm.org/LICENSE.txt for license information.
57330f729Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67330f729Sjoerg //
77330f729Sjoerg //===----------------------------------------------------------------------===//
87330f729Sjoerg //
97330f729Sjoerg // This file implements a tool for syntax tree based comparison using
107330f729Sjoerg // Tooling/ASTDiff.
117330f729Sjoerg //
127330f729Sjoerg //===----------------------------------------------------------------------===//
137330f729Sjoerg 
147330f729Sjoerg #include "clang/Tooling/ASTDiff/ASTDiff.h"
157330f729Sjoerg #include "clang/Tooling/CommonOptionsParser.h"
167330f729Sjoerg #include "clang/Tooling/Tooling.h"
177330f729Sjoerg #include "llvm/Support/CommandLine.h"
187330f729Sjoerg 
197330f729Sjoerg using namespace llvm;
207330f729Sjoerg using namespace clang;
217330f729Sjoerg using namespace clang::tooling;
227330f729Sjoerg 
237330f729Sjoerg static cl::OptionCategory ClangDiffCategory("clang-diff options");
247330f729Sjoerg 
257330f729Sjoerg static cl::opt<bool>
267330f729Sjoerg     ASTDump("ast-dump",
277330f729Sjoerg             cl::desc("Print the internal representation of the AST."),
287330f729Sjoerg             cl::init(false), cl::cat(ClangDiffCategory));
297330f729Sjoerg 
307330f729Sjoerg static cl::opt<bool> ASTDumpJson(
317330f729Sjoerg     "ast-dump-json",
327330f729Sjoerg     cl::desc("Print the internal representation of the AST as JSON."),
337330f729Sjoerg     cl::init(false), cl::cat(ClangDiffCategory));
347330f729Sjoerg 
357330f729Sjoerg static cl::opt<bool> PrintMatches("dump-matches",
367330f729Sjoerg                                   cl::desc("Print the matched nodes."),
377330f729Sjoerg                                   cl::init(false), cl::cat(ClangDiffCategory));
387330f729Sjoerg 
397330f729Sjoerg static cl::opt<bool> HtmlDiff("html",
407330f729Sjoerg                               cl::desc("Output a side-by-side diff in HTML."),
417330f729Sjoerg                               cl::init(false), cl::cat(ClangDiffCategory));
427330f729Sjoerg 
437330f729Sjoerg static cl::opt<std::string> SourcePath(cl::Positional, cl::desc("<source>"),
447330f729Sjoerg                                        cl::Required,
457330f729Sjoerg                                        cl::cat(ClangDiffCategory));
467330f729Sjoerg 
477330f729Sjoerg static cl::opt<std::string> DestinationPath(cl::Positional,
487330f729Sjoerg                                             cl::desc("<destination>"),
497330f729Sjoerg                                             cl::Optional,
507330f729Sjoerg                                             cl::cat(ClangDiffCategory));
517330f729Sjoerg 
527330f729Sjoerg static cl::opt<std::string> StopAfter("stop-diff-after",
537330f729Sjoerg                                       cl::desc("<topdown|bottomup>"),
547330f729Sjoerg                                       cl::Optional, cl::init(""),
557330f729Sjoerg                                       cl::cat(ClangDiffCategory));
567330f729Sjoerg 
577330f729Sjoerg static cl::opt<int> MaxSize("s", cl::desc("<maxsize>"), cl::Optional,
587330f729Sjoerg                             cl::init(-1), cl::cat(ClangDiffCategory));
597330f729Sjoerg 
607330f729Sjoerg static cl::opt<std::string> BuildPath("p", cl::desc("Build path"), cl::init(""),
617330f729Sjoerg                                       cl::Optional, cl::cat(ClangDiffCategory));
627330f729Sjoerg 
637330f729Sjoerg static cl::list<std::string> ArgsAfter(
647330f729Sjoerg     "extra-arg",
657330f729Sjoerg     cl::desc("Additional argument to append to the compiler command line"),
667330f729Sjoerg     cl::cat(ClangDiffCategory));
677330f729Sjoerg 
687330f729Sjoerg static cl::list<std::string> ArgsBefore(
697330f729Sjoerg     "extra-arg-before",
707330f729Sjoerg     cl::desc("Additional argument to prepend to the compiler command line"),
717330f729Sjoerg     cl::cat(ClangDiffCategory));
727330f729Sjoerg 
addExtraArgs(std::unique_ptr<CompilationDatabase> & Compilations)737330f729Sjoerg static void addExtraArgs(std::unique_ptr<CompilationDatabase> &Compilations) {
747330f729Sjoerg   if (!Compilations)
757330f729Sjoerg     return;
767330f729Sjoerg   auto AdjustingCompilations =
777330f729Sjoerg       std::make_unique<ArgumentsAdjustingCompilations>(
787330f729Sjoerg           std::move(Compilations));
797330f729Sjoerg   AdjustingCompilations->appendArgumentsAdjuster(
807330f729Sjoerg       getInsertArgumentAdjuster(ArgsBefore, ArgumentInsertPosition::BEGIN));
817330f729Sjoerg   AdjustingCompilations->appendArgumentsAdjuster(
827330f729Sjoerg       getInsertArgumentAdjuster(ArgsAfter, ArgumentInsertPosition::END));
837330f729Sjoerg   Compilations = std::move(AdjustingCompilations);
847330f729Sjoerg }
857330f729Sjoerg 
867330f729Sjoerg static std::unique_ptr<ASTUnit>
getAST(const std::unique_ptr<CompilationDatabase> & CommonCompilations,const StringRef Filename)877330f729Sjoerg getAST(const std::unique_ptr<CompilationDatabase> &CommonCompilations,
887330f729Sjoerg        const StringRef Filename) {
897330f729Sjoerg   std::string ErrorMessage;
907330f729Sjoerg   std::unique_ptr<CompilationDatabase> Compilations;
917330f729Sjoerg   if (!CommonCompilations) {
927330f729Sjoerg     Compilations = CompilationDatabase::autoDetectFromSource(
937330f729Sjoerg         BuildPath.empty() ? Filename : BuildPath, ErrorMessage);
947330f729Sjoerg     if (!Compilations) {
957330f729Sjoerg       llvm::errs()
967330f729Sjoerg           << "Error while trying to load a compilation database, running "
977330f729Sjoerg              "without flags.\n"
987330f729Sjoerg           << ErrorMessage;
997330f729Sjoerg       Compilations =
1007330f729Sjoerg           std::make_unique<clang::tooling::FixedCompilationDatabase>(
1017330f729Sjoerg               ".", std::vector<std::string>());
1027330f729Sjoerg     }
1037330f729Sjoerg   }
1047330f729Sjoerg   addExtraArgs(Compilations);
105*e038c9c4Sjoerg   std::array<std::string, 1> Files = {{std::string(Filename)}};
1067330f729Sjoerg   ClangTool Tool(Compilations ? *Compilations : *CommonCompilations, Files);
1077330f729Sjoerg   std::vector<std::unique_ptr<ASTUnit>> ASTs;
1087330f729Sjoerg   Tool.buildASTs(ASTs);
1097330f729Sjoerg   if (ASTs.size() != Files.size())
1107330f729Sjoerg     return nullptr;
1117330f729Sjoerg   return std::move(ASTs[0]);
1127330f729Sjoerg }
1137330f729Sjoerg 
hexdigit(int N)1147330f729Sjoerg static char hexdigit(int N) { return N &= 0xf, N + (N < 10 ? '0' : 'a' - 10); }
1157330f729Sjoerg 
1167330f729Sjoerg static const char HtmlDiffHeader[] = R"(
1177330f729Sjoerg <html>
1187330f729Sjoerg <head>
1197330f729Sjoerg <meta charset='utf-8'/>
1207330f729Sjoerg <style>
1217330f729Sjoerg span.d { color: red; }
1227330f729Sjoerg span.u { color: #cc00cc; }
1237330f729Sjoerg span.i { color: green; }
1247330f729Sjoerg span.m { font-weight: bold; }
1257330f729Sjoerg span   { font-weight: normal; color: black; }
1267330f729Sjoerg div.code {
1277330f729Sjoerg   width: 48%;
1287330f729Sjoerg   height: 98%;
1297330f729Sjoerg   overflow: scroll;
1307330f729Sjoerg   float: left;
1317330f729Sjoerg   padding: 0 0 0.5% 0.5%;
1327330f729Sjoerg   border: solid 2px LightGrey;
1337330f729Sjoerg   border-radius: 5px;
1347330f729Sjoerg }
1357330f729Sjoerg </style>
1367330f729Sjoerg </head>
1377330f729Sjoerg <script type='text/javascript'>
1387330f729Sjoerg highlightStack = []
1397330f729Sjoerg function clearHighlight() {
1407330f729Sjoerg   while (highlightStack.length) {
1417330f729Sjoerg     var [l, r] = highlightStack.pop()
1427330f729Sjoerg     document.getElementById(l).style.backgroundColor = 'inherit'
1437330f729Sjoerg     if (r[1] != '-')
1447330f729Sjoerg       document.getElementById(r).style.backgroundColor = 'inherit'
1457330f729Sjoerg   }
1467330f729Sjoerg }
1477330f729Sjoerg function highlight(event) {
1487330f729Sjoerg   var id = event.target['id']
1497330f729Sjoerg   doHighlight(id)
1507330f729Sjoerg }
1517330f729Sjoerg function doHighlight(id) {
1527330f729Sjoerg   clearHighlight()
1537330f729Sjoerg   source = document.getElementById(id)
1547330f729Sjoerg   if (!source.attributes['tid'])
1557330f729Sjoerg     return
1567330f729Sjoerg   var mapped = source
1577330f729Sjoerg   while (mapped && mapped.parentElement && mapped.attributes['tid'].value.substr(1) === '-1')
1587330f729Sjoerg     mapped = mapped.parentElement
1597330f729Sjoerg   var tid = null, target = null
1607330f729Sjoerg   if (mapped) {
1617330f729Sjoerg     tid = mapped.attributes['tid'].value
1627330f729Sjoerg     target = document.getElementById(tid)
1637330f729Sjoerg   }
1647330f729Sjoerg   if (source.parentElement && source.parentElement.classList.contains('code'))
1657330f729Sjoerg     return
1667330f729Sjoerg   source.style.backgroundColor = 'lightgrey'
1677330f729Sjoerg   source.scrollIntoView()
1687330f729Sjoerg   if (target) {
1697330f729Sjoerg     if (mapped === source)
1707330f729Sjoerg       target.style.backgroundColor = 'lightgrey'
1717330f729Sjoerg     target.scrollIntoView()
1727330f729Sjoerg   }
1737330f729Sjoerg   highlightStack.push([id, tid])
1747330f729Sjoerg   location.hash = '#' + id
1757330f729Sjoerg }
1767330f729Sjoerg function scrollToBoth() {
1777330f729Sjoerg   doHighlight(location.hash.substr(1))
1787330f729Sjoerg }
1797330f729Sjoerg function changed(elem) {
1807330f729Sjoerg   return elem.classList.length == 0
1817330f729Sjoerg }
1827330f729Sjoerg function nextChangedNode(prefix, increment, number) {
1837330f729Sjoerg   do {
1847330f729Sjoerg     number += increment
1857330f729Sjoerg     var elem = document.getElementById(prefix + number)
1867330f729Sjoerg   } while(elem && !changed(elem))
1877330f729Sjoerg   return elem ? number : null
1887330f729Sjoerg }
1897330f729Sjoerg function handleKey(e) {
1907330f729Sjoerg   var down = e.code === "KeyJ"
1917330f729Sjoerg   var up = e.code === "KeyK"
1927330f729Sjoerg   if (!down && !up)
1937330f729Sjoerg     return
1947330f729Sjoerg   var id = highlightStack[0] ? highlightStack[0][0] : 'R0'
1957330f729Sjoerg   var oldelem = document.getElementById(id)
1967330f729Sjoerg   var number = parseInt(id.substr(1))
1977330f729Sjoerg   var increment = down ? 1 : -1
1987330f729Sjoerg   var lastnumber = number
1997330f729Sjoerg   var prefix = id[0]
2007330f729Sjoerg   do {
2017330f729Sjoerg     number = nextChangedNode(prefix, increment, number)
2027330f729Sjoerg     var elem = document.getElementById(prefix + number)
2037330f729Sjoerg     if (up && elem) {
2047330f729Sjoerg       while (elem.parentElement && changed(elem.parentElement))
2057330f729Sjoerg         elem = elem.parentElement
2067330f729Sjoerg       number = elem.id.substr(1)
2077330f729Sjoerg     }
2087330f729Sjoerg   } while ((down && id !== 'R0' && oldelem.contains(elem)))
2097330f729Sjoerg   if (!number)
2107330f729Sjoerg     number = lastnumber
2117330f729Sjoerg   elem = document.getElementById(prefix + number)
2127330f729Sjoerg   doHighlight(prefix + number)
2137330f729Sjoerg }
2147330f729Sjoerg window.onload = scrollToBoth
2157330f729Sjoerg window.onkeydown = handleKey
2167330f729Sjoerg </script>
2177330f729Sjoerg <body>
2187330f729Sjoerg <div onclick='highlight(event)'>
2197330f729Sjoerg )";
2207330f729Sjoerg 
printHtml(raw_ostream & OS,char C)2217330f729Sjoerg static void printHtml(raw_ostream &OS, char C) {
2227330f729Sjoerg   switch (C) {
2237330f729Sjoerg   case '&':
2247330f729Sjoerg     OS << "&amp;";
2257330f729Sjoerg     break;
2267330f729Sjoerg   case '<':
2277330f729Sjoerg     OS << "&lt;";
2287330f729Sjoerg     break;
2297330f729Sjoerg   case '>':
2307330f729Sjoerg     OS << "&gt;";
2317330f729Sjoerg     break;
2327330f729Sjoerg   case '\'':
2337330f729Sjoerg     OS << "&#x27;";
2347330f729Sjoerg     break;
2357330f729Sjoerg   case '"':
2367330f729Sjoerg     OS << "&quot;";
2377330f729Sjoerg     break;
2387330f729Sjoerg   default:
2397330f729Sjoerg     OS << C;
2407330f729Sjoerg   }
2417330f729Sjoerg }
2427330f729Sjoerg 
printHtml(raw_ostream & OS,const StringRef Str)2437330f729Sjoerg static void printHtml(raw_ostream &OS, const StringRef Str) {
2447330f729Sjoerg   for (char C : Str)
2457330f729Sjoerg     printHtml(OS, C);
2467330f729Sjoerg }
2477330f729Sjoerg 
getChangeKindAbbr(diff::ChangeKind Kind)2487330f729Sjoerg static std::string getChangeKindAbbr(diff::ChangeKind Kind) {
2497330f729Sjoerg   switch (Kind) {
2507330f729Sjoerg   case diff::None:
2517330f729Sjoerg     return "";
2527330f729Sjoerg   case diff::Delete:
2537330f729Sjoerg     return "d";
2547330f729Sjoerg   case diff::Update:
2557330f729Sjoerg     return "u";
2567330f729Sjoerg   case diff::Insert:
2577330f729Sjoerg     return "i";
2587330f729Sjoerg   case diff::Move:
2597330f729Sjoerg     return "m";
2607330f729Sjoerg   case diff::UpdateMove:
2617330f729Sjoerg     return "u m";
2627330f729Sjoerg   }
2637330f729Sjoerg   llvm_unreachable("Invalid enumeration value.");
2647330f729Sjoerg }
2657330f729Sjoerg 
printHtmlForNode(raw_ostream & OS,const diff::ASTDiff & Diff,diff::SyntaxTree & Tree,bool IsLeft,diff::NodeId Id,unsigned Offset)2667330f729Sjoerg static unsigned printHtmlForNode(raw_ostream &OS, const diff::ASTDiff &Diff,
2677330f729Sjoerg                                  diff::SyntaxTree &Tree, bool IsLeft,
2687330f729Sjoerg                                  diff::NodeId Id, unsigned Offset) {
2697330f729Sjoerg   const diff::Node &Node = Tree.getNode(Id);
2707330f729Sjoerg   char MyTag, OtherTag;
2717330f729Sjoerg   diff::NodeId LeftId, RightId;
2727330f729Sjoerg   diff::NodeId TargetId = Diff.getMapped(Tree, Id);
2737330f729Sjoerg   if (IsLeft) {
2747330f729Sjoerg     MyTag = 'L';
2757330f729Sjoerg     OtherTag = 'R';
2767330f729Sjoerg     LeftId = Id;
2777330f729Sjoerg     RightId = TargetId;
2787330f729Sjoerg   } else {
2797330f729Sjoerg     MyTag = 'R';
2807330f729Sjoerg     OtherTag = 'L';
2817330f729Sjoerg     LeftId = TargetId;
2827330f729Sjoerg     RightId = Id;
2837330f729Sjoerg   }
2847330f729Sjoerg   unsigned Begin, End;
2857330f729Sjoerg   std::tie(Begin, End) = Tree.getSourceRangeOffsets(Node);
2867330f729Sjoerg   const SourceManager &SrcMgr = Tree.getASTContext().getSourceManager();
287*e038c9c4Sjoerg   auto Code = SrcMgr.getBufferOrFake(SrcMgr.getMainFileID()).getBuffer();
2887330f729Sjoerg   for (; Offset < Begin; ++Offset)
2897330f729Sjoerg     printHtml(OS, Code[Offset]);
2907330f729Sjoerg   OS << "<span id='" << MyTag << Id << "' "
2917330f729Sjoerg      << "tid='" << OtherTag << TargetId << "' ";
2927330f729Sjoerg   OS << "title='";
2937330f729Sjoerg   printHtml(OS, Node.getTypeLabel());
2947330f729Sjoerg   OS << "\n" << LeftId << " -> " << RightId;
2957330f729Sjoerg   std::string Value = Tree.getNodeValue(Node);
2967330f729Sjoerg   if (!Value.empty()) {
2977330f729Sjoerg     OS << "\n";
2987330f729Sjoerg     printHtml(OS, Value);
2997330f729Sjoerg   }
3007330f729Sjoerg   OS << "'";
3017330f729Sjoerg   if (Node.Change != diff::None)
3027330f729Sjoerg     OS << " class='" << getChangeKindAbbr(Node.Change) << "'";
3037330f729Sjoerg   OS << ">";
3047330f729Sjoerg 
3057330f729Sjoerg   for (diff::NodeId Child : Node.Children)
3067330f729Sjoerg     Offset = printHtmlForNode(OS, Diff, Tree, IsLeft, Child, Offset);
3077330f729Sjoerg 
3087330f729Sjoerg   for (; Offset < End; ++Offset)
3097330f729Sjoerg     printHtml(OS, Code[Offset]);
3107330f729Sjoerg   if (Id == Tree.getRootId()) {
3117330f729Sjoerg     End = Code.size();
3127330f729Sjoerg     for (; Offset < End; ++Offset)
3137330f729Sjoerg       printHtml(OS, Code[Offset]);
3147330f729Sjoerg   }
3157330f729Sjoerg   OS << "</span>";
3167330f729Sjoerg   return Offset;
3177330f729Sjoerg }
3187330f729Sjoerg 
printJsonString(raw_ostream & OS,const StringRef Str)3197330f729Sjoerg static void printJsonString(raw_ostream &OS, const StringRef Str) {
3207330f729Sjoerg   for (signed char C : Str) {
3217330f729Sjoerg     switch (C) {
3227330f729Sjoerg     case '"':
3237330f729Sjoerg       OS << R"(\")";
3247330f729Sjoerg       break;
3257330f729Sjoerg     case '\\':
3267330f729Sjoerg       OS << R"(\\)";
3277330f729Sjoerg       break;
3287330f729Sjoerg     case '\n':
3297330f729Sjoerg       OS << R"(\n)";
3307330f729Sjoerg       break;
3317330f729Sjoerg     case '\t':
3327330f729Sjoerg       OS << R"(\t)";
3337330f729Sjoerg       break;
3347330f729Sjoerg     default:
3357330f729Sjoerg       if ('\x00' <= C && C <= '\x1f') {
3367330f729Sjoerg         OS << R"(\u00)" << hexdigit(C >> 4) << hexdigit(C);
3377330f729Sjoerg       } else {
3387330f729Sjoerg         OS << C;
3397330f729Sjoerg       }
3407330f729Sjoerg     }
3417330f729Sjoerg   }
3427330f729Sjoerg }
3437330f729Sjoerg 
printNodeAttributes(raw_ostream & OS,diff::SyntaxTree & Tree,diff::NodeId Id)3447330f729Sjoerg static void printNodeAttributes(raw_ostream &OS, diff::SyntaxTree &Tree,
3457330f729Sjoerg                                 diff::NodeId Id) {
3467330f729Sjoerg   const diff::Node &N = Tree.getNode(Id);
3477330f729Sjoerg   OS << R"("id":)" << int(Id);
3487330f729Sjoerg   OS << R"(,"type":")" << N.getTypeLabel() << '"';
3497330f729Sjoerg   auto Offsets = Tree.getSourceRangeOffsets(N);
3507330f729Sjoerg   OS << R"(,"begin":)" << Offsets.first;
3517330f729Sjoerg   OS << R"(,"end":)" << Offsets.second;
3527330f729Sjoerg   std::string Value = Tree.getNodeValue(N);
3537330f729Sjoerg   if (!Value.empty()) {
3547330f729Sjoerg     OS << R"(,"value":")";
3557330f729Sjoerg     printJsonString(OS, Value);
3567330f729Sjoerg     OS << '"';
3577330f729Sjoerg   }
3587330f729Sjoerg }
3597330f729Sjoerg 
3607330f729Sjoerg static void printNodeAsJson(raw_ostream &OS, diff::SyntaxTree &Tree,
3617330f729Sjoerg                             diff::NodeId Id) {
3627330f729Sjoerg   const diff::Node &N = Tree.getNode(Id);
3637330f729Sjoerg   OS << "{";
3647330f729Sjoerg   printNodeAttributes(OS, Tree, Id);
3657330f729Sjoerg   auto Identifier = N.getIdentifier();
3667330f729Sjoerg   auto QualifiedIdentifier = N.getQualifiedIdentifier();
3677330f729Sjoerg   if (Identifier) {
3687330f729Sjoerg     OS << R"(,"identifier":")";
3697330f729Sjoerg     printJsonString(OS, *Identifier);
3707330f729Sjoerg     OS << R"(")";
3717330f729Sjoerg     if (QualifiedIdentifier && *Identifier != *QualifiedIdentifier) {
3727330f729Sjoerg       OS << R"(,"qualified_identifier":")";
3737330f729Sjoerg       printJsonString(OS, *QualifiedIdentifier);
3747330f729Sjoerg       OS << R"(")";
3757330f729Sjoerg     }
3767330f729Sjoerg   }
3777330f729Sjoerg   OS << R"(,"children":[)";
3787330f729Sjoerg   if (N.Children.size() > 0) {
3797330f729Sjoerg     printNodeAsJson(OS, Tree, N.Children[0]);
3807330f729Sjoerg     for (size_t I = 1, E = N.Children.size(); I < E; ++I) {
3817330f729Sjoerg       OS << ",";
3827330f729Sjoerg       printNodeAsJson(OS, Tree, N.Children[I]);
3837330f729Sjoerg     }
3847330f729Sjoerg   }
3857330f729Sjoerg   OS << "]}";
3867330f729Sjoerg }
3877330f729Sjoerg 
3887330f729Sjoerg static void printNode(raw_ostream &OS, diff::SyntaxTree &Tree,
3897330f729Sjoerg                       diff::NodeId Id) {
3907330f729Sjoerg   if (Id.isInvalid()) {
3917330f729Sjoerg     OS << "None";
3927330f729Sjoerg     return;
3937330f729Sjoerg   }
3947330f729Sjoerg   OS << Tree.getNode(Id).getTypeLabel();
3957330f729Sjoerg   std::string Value = Tree.getNodeValue(Id);
3967330f729Sjoerg   if (!Value.empty())
3977330f729Sjoerg     OS << ": " << Value;
3987330f729Sjoerg   OS << "(" << Id << ")";
3997330f729Sjoerg }
4007330f729Sjoerg 
4017330f729Sjoerg static void printTree(raw_ostream &OS, diff::SyntaxTree &Tree) {
4027330f729Sjoerg   for (diff::NodeId Id : Tree) {
4037330f729Sjoerg     for (int I = 0; I < Tree.getNode(Id).Depth; ++I)
4047330f729Sjoerg       OS << " ";
4057330f729Sjoerg     printNode(OS, Tree, Id);
4067330f729Sjoerg     OS << "\n";
4077330f729Sjoerg   }
4087330f729Sjoerg }
4097330f729Sjoerg 
4107330f729Sjoerg static void printDstChange(raw_ostream &OS, diff::ASTDiff &Diff,
4117330f729Sjoerg                            diff::SyntaxTree &SrcTree, diff::SyntaxTree &DstTree,
4127330f729Sjoerg                            diff::NodeId Dst) {
4137330f729Sjoerg   const diff::Node &DstNode = DstTree.getNode(Dst);
4147330f729Sjoerg   diff::NodeId Src = Diff.getMapped(DstTree, Dst);
4157330f729Sjoerg   switch (DstNode.Change) {
4167330f729Sjoerg   case diff::None:
4177330f729Sjoerg     break;
4187330f729Sjoerg   case diff::Delete:
4197330f729Sjoerg     llvm_unreachable("The destination tree can't have deletions.");
4207330f729Sjoerg   case diff::Update:
4217330f729Sjoerg     OS << "Update ";
4227330f729Sjoerg     printNode(OS, SrcTree, Src);
4237330f729Sjoerg     OS << " to " << DstTree.getNodeValue(Dst) << "\n";
4247330f729Sjoerg     break;
4257330f729Sjoerg   case diff::Insert:
4267330f729Sjoerg   case diff::Move:
4277330f729Sjoerg   case diff::UpdateMove:
4287330f729Sjoerg     if (DstNode.Change == diff::Insert)
4297330f729Sjoerg       OS << "Insert";
4307330f729Sjoerg     else if (DstNode.Change == diff::Move)
4317330f729Sjoerg       OS << "Move";
4327330f729Sjoerg     else if (DstNode.Change == diff::UpdateMove)
4337330f729Sjoerg       OS << "Update and Move";
4347330f729Sjoerg     OS << " ";
4357330f729Sjoerg     printNode(OS, DstTree, Dst);
4367330f729Sjoerg     OS << " into ";
4377330f729Sjoerg     printNode(OS, DstTree, DstNode.Parent);
4387330f729Sjoerg     OS << " at " << DstTree.findPositionInParent(Dst) << "\n";
4397330f729Sjoerg     break;
4407330f729Sjoerg   }
4417330f729Sjoerg }
4427330f729Sjoerg 
4437330f729Sjoerg int main(int argc, const char **argv) {
4447330f729Sjoerg   std::string ErrorMessage;
4457330f729Sjoerg   std::unique_ptr<CompilationDatabase> CommonCompilations =
4467330f729Sjoerg       FixedCompilationDatabase::loadFromCommandLine(argc, argv, ErrorMessage);
4477330f729Sjoerg   if (!CommonCompilations && !ErrorMessage.empty())
4487330f729Sjoerg     llvm::errs() << ErrorMessage;
4497330f729Sjoerg   cl::HideUnrelatedOptions(ClangDiffCategory);
4507330f729Sjoerg   if (!cl::ParseCommandLineOptions(argc, argv)) {
4517330f729Sjoerg     cl::PrintOptionValues();
4527330f729Sjoerg     return 1;
4537330f729Sjoerg   }
4547330f729Sjoerg 
4557330f729Sjoerg   addExtraArgs(CommonCompilations);
4567330f729Sjoerg 
4577330f729Sjoerg   if (ASTDump || ASTDumpJson) {
4587330f729Sjoerg     if (!DestinationPath.empty()) {
4597330f729Sjoerg       llvm::errs() << "Error: Please specify exactly one filename.\n";
4607330f729Sjoerg       return 1;
4617330f729Sjoerg     }
4627330f729Sjoerg     std::unique_ptr<ASTUnit> AST = getAST(CommonCompilations, SourcePath);
4637330f729Sjoerg     if (!AST)
4647330f729Sjoerg       return 1;
4657330f729Sjoerg     diff::SyntaxTree Tree(AST->getASTContext());
4667330f729Sjoerg     if (ASTDump) {
4677330f729Sjoerg       printTree(llvm::outs(), Tree);
4687330f729Sjoerg       return 0;
4697330f729Sjoerg     }
4707330f729Sjoerg     llvm::outs() << R"({"filename":")";
4717330f729Sjoerg     printJsonString(llvm::outs(), SourcePath);
4727330f729Sjoerg     llvm::outs() << R"(","root":)";
4737330f729Sjoerg     printNodeAsJson(llvm::outs(), Tree, Tree.getRootId());
4747330f729Sjoerg     llvm::outs() << "}\n";
4757330f729Sjoerg     return 0;
4767330f729Sjoerg   }
4777330f729Sjoerg 
4787330f729Sjoerg   if (DestinationPath.empty()) {
4797330f729Sjoerg     llvm::errs() << "Error: Exactly two paths are required.\n";
4807330f729Sjoerg     return 1;
4817330f729Sjoerg   }
4827330f729Sjoerg 
4837330f729Sjoerg   std::unique_ptr<ASTUnit> Src = getAST(CommonCompilations, SourcePath);
4847330f729Sjoerg   std::unique_ptr<ASTUnit> Dst = getAST(CommonCompilations, DestinationPath);
4857330f729Sjoerg   if (!Src || !Dst)
4867330f729Sjoerg     return 1;
4877330f729Sjoerg 
4887330f729Sjoerg   diff::ComparisonOptions Options;
4897330f729Sjoerg   if (MaxSize != -1)
4907330f729Sjoerg     Options.MaxSize = MaxSize;
4917330f729Sjoerg   if (!StopAfter.empty()) {
4927330f729Sjoerg     if (StopAfter == "topdown")
4937330f729Sjoerg       Options.StopAfterTopDown = true;
4947330f729Sjoerg     else if (StopAfter != "bottomup") {
4957330f729Sjoerg       llvm::errs() << "Error: Invalid argument for -stop-after\n";
4967330f729Sjoerg       return 1;
4977330f729Sjoerg     }
4987330f729Sjoerg   }
4997330f729Sjoerg   diff::SyntaxTree SrcTree(Src->getASTContext());
5007330f729Sjoerg   diff::SyntaxTree DstTree(Dst->getASTContext());
5017330f729Sjoerg   diff::ASTDiff Diff(SrcTree, DstTree, Options);
5027330f729Sjoerg 
5037330f729Sjoerg   if (HtmlDiff) {
5047330f729Sjoerg     llvm::outs() << HtmlDiffHeader << "<pre>";
5057330f729Sjoerg     llvm::outs() << "<div id='L' class='code'>";
5067330f729Sjoerg     printHtmlForNode(llvm::outs(), Diff, SrcTree, true, SrcTree.getRootId(), 0);
5077330f729Sjoerg     llvm::outs() << "</div>";
5087330f729Sjoerg     llvm::outs() << "<div id='R' class='code'>";
5097330f729Sjoerg     printHtmlForNode(llvm::outs(), Diff, DstTree, false, DstTree.getRootId(),
5107330f729Sjoerg                      0);
5117330f729Sjoerg     llvm::outs() << "</div>";
5127330f729Sjoerg     llvm::outs() << "</pre></div></body></html>\n";
5137330f729Sjoerg     return 0;
5147330f729Sjoerg   }
5157330f729Sjoerg 
5167330f729Sjoerg   for (diff::NodeId Dst : DstTree) {
5177330f729Sjoerg     diff::NodeId Src = Diff.getMapped(DstTree, Dst);
5187330f729Sjoerg     if (PrintMatches && Src.isValid()) {
5197330f729Sjoerg       llvm::outs() << "Match ";
5207330f729Sjoerg       printNode(llvm::outs(), SrcTree, Src);
5217330f729Sjoerg       llvm::outs() << " to ";
5227330f729Sjoerg       printNode(llvm::outs(), DstTree, Dst);
5237330f729Sjoerg       llvm::outs() << "\n";
5247330f729Sjoerg     }
5257330f729Sjoerg     printDstChange(llvm::outs(), Diff, SrcTree, DstTree, Dst);
5267330f729Sjoerg   }
5277330f729Sjoerg   for (diff::NodeId Src : SrcTree) {
5287330f729Sjoerg     if (Diff.getMapped(SrcTree, Src).isInvalid()) {
5297330f729Sjoerg       llvm::outs() << "Delete ";
5307330f729Sjoerg       printNode(llvm::outs(), SrcTree, Src);
5317330f729Sjoerg       llvm::outs() << "\n";
5327330f729Sjoerg     }
5337330f729Sjoerg   }
5347330f729Sjoerg 
5357330f729Sjoerg   return 0;
5367330f729Sjoerg }
537