xref: /openbsd-src/gnu/llvm/clang/tools/clang-diff/ClangDiff.cpp (revision e5dd70708596ae51455a0ffa086a00c5b29f8583)
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 << "&amp;";
225*e5dd7070Spatrick     break;
226*e5dd7070Spatrick   case '<':
227*e5dd7070Spatrick     OS << "&lt;";
228*e5dd7070Spatrick     break;
229*e5dd7070Spatrick   case '>':
230*e5dd7070Spatrick     OS << "&gt;";
231*e5dd7070Spatrick     break;
232*e5dd7070Spatrick   case '\'':
233*e5dd7070Spatrick     OS << "&#x27;";
234*e5dd7070Spatrick     break;
235*e5dd7070Spatrick   case '"':
236*e5dd7070Spatrick     OS << "&quot;";
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