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 
addExtraArgs(std::unique_ptr<CompilationDatabase> & Compilations)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>
getAST(const std::unique_ptr<CompilationDatabase> & CommonCompilations,const StringRef Filename)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);
105ec727ea7Spatrick   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 
hexdigit(int N)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 
printHtml(raw_ostream & OS,char C)221e5dd7070Spatrick static void printHtml(raw_ostream &OS, char C) {
222e5dd7070Spatrick   switch (C) {
223e5dd7070Spatrick   case '&':
224e5dd7070Spatrick     OS << "&amp;";
225e5dd7070Spatrick     break;
226e5dd7070Spatrick   case '<':
227e5dd7070Spatrick     OS << "&lt;";
228e5dd7070Spatrick     break;
229e5dd7070Spatrick   case '>':
230e5dd7070Spatrick     OS << "&gt;";
231e5dd7070Spatrick     break;
232e5dd7070Spatrick   case '\'':
233e5dd7070Spatrick     OS << "&#x27;";
234e5dd7070Spatrick     break;
235e5dd7070Spatrick   case '"':
236e5dd7070Spatrick     OS << "&quot;";
237e5dd7070Spatrick     break;
238e5dd7070Spatrick   default:
239e5dd7070Spatrick     OS << C;
240e5dd7070Spatrick   }
241e5dd7070Spatrick }
242e5dd7070Spatrick 
printHtml(raw_ostream & OS,const StringRef Str)243e5dd7070Spatrick static void printHtml(raw_ostream &OS, const StringRef Str) {
244e5dd7070Spatrick   for (char C : Str)
245e5dd7070Spatrick     printHtml(OS, C);
246e5dd7070Spatrick }
247e5dd7070Spatrick 
getChangeKindAbbr(diff::ChangeKind Kind)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 
printHtmlForNode(raw_ostream & OS,const diff::ASTDiff & Diff,diff::SyntaxTree & Tree,bool IsLeft,diff::NodeId Id,unsigned Offset)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();
287*a9ac8606Spatrick   auto Code = SrcMgr.getBufferOrFake(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 
printJsonString(raw_ostream & OS,const StringRef Str)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 
printNodeAttributes(raw_ostream & OS,diff::SyntaxTree & Tree,diff::NodeId Id)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