xref: /llvm-project/clang/tools/clang-diff/ClangDiff.cpp (revision a29d6aecedfefde83daaf5858c13f0f4ce7f2aaa)
1a75b2cacSAlex Lorenz //===- ClangDiff.cpp - compare source files by AST nodes ------*- C++ -*- -===//
2a75b2cacSAlex Lorenz //
3a75b2cacSAlex Lorenz //                     The LLVM Compiler Infrastructure
4a75b2cacSAlex Lorenz //
5a75b2cacSAlex Lorenz // This file is distributed under the University of Illinois Open Source
6a75b2cacSAlex Lorenz // License. See LICENSE.TXT for details.
7a75b2cacSAlex Lorenz //
8a75b2cacSAlex Lorenz //===----------------------------------------------------------------------===//
9a75b2cacSAlex Lorenz //
10a75b2cacSAlex Lorenz // This file implements a tool for syntax tree based comparison using
11a75b2cacSAlex Lorenz // Tooling/ASTDiff.
12a75b2cacSAlex Lorenz //
13a75b2cacSAlex Lorenz //===----------------------------------------------------------------------===//
14a75b2cacSAlex Lorenz 
15a75b2cacSAlex Lorenz #include "clang/Tooling/ASTDiff/ASTDiff.h"
16a75b2cacSAlex Lorenz #include "clang/Tooling/CommonOptionsParser.h"
17a75b2cacSAlex Lorenz #include "clang/Tooling/Tooling.h"
18a75b2cacSAlex Lorenz #include "llvm/Support/CommandLine.h"
19a75b2cacSAlex Lorenz 
20a75b2cacSAlex Lorenz using namespace llvm;
21a75b2cacSAlex Lorenz using namespace clang;
22a75b2cacSAlex Lorenz using namespace clang::tooling;
23a75b2cacSAlex Lorenz 
24a75b2cacSAlex Lorenz static cl::OptionCategory ClangDiffCategory("clang-diff options");
25a75b2cacSAlex Lorenz 
26a75b2cacSAlex Lorenz static cl::opt<bool>
27914a958eSJohannes Altmanninger     ASTDump("ast-dump",
28a1d2b5d5SJohannes Altmanninger             cl::desc("Print the internal representation of the AST."),
29a1d2b5d5SJohannes Altmanninger             cl::init(false), cl::cat(ClangDiffCategory));
30a1d2b5d5SJohannes Altmanninger 
31a1d2b5d5SJohannes Altmanninger static cl::opt<bool> ASTDumpJson(
32a1d2b5d5SJohannes Altmanninger     "ast-dump-json",
33a75b2cacSAlex Lorenz     cl::desc("Print the internal representation of the AST as JSON."),
34a75b2cacSAlex Lorenz     cl::init(false), cl::cat(ClangDiffCategory));
35a75b2cacSAlex Lorenz 
36683876caSJohannes Altmanninger static cl::opt<bool>
37683876caSJohannes Altmanninger     PrintMatches("dump-matches", cl::desc("Print the matched nodes."),
38683876caSJohannes Altmanninger                  cl::init(false), cl::cat(ClangDiffCategory));
39683876caSJohannes Altmanninger 
40*a29d6aecSJohannes Altmanninger static cl::opt<bool> HtmlDiff("html",
41*a29d6aecSJohannes Altmanninger                               cl::desc("Output a side-by-side diff in HTML."),
42*a29d6aecSJohannes Altmanninger                               cl::init(false), cl::cat(ClangDiffCategory));
43*a29d6aecSJohannes Altmanninger 
44a75b2cacSAlex Lorenz static cl::opt<std::string> SourcePath(cl::Positional, cl::desc("<source>"),
45a75b2cacSAlex Lorenz                                        cl::Required,
46a75b2cacSAlex Lorenz                                        cl::cat(ClangDiffCategory));
47a75b2cacSAlex Lorenz 
48a75b2cacSAlex Lorenz static cl::opt<std::string> DestinationPath(cl::Positional,
49a75b2cacSAlex Lorenz                                             cl::desc("<destination>"),
50a75b2cacSAlex Lorenz                                             cl::Optional,
51a75b2cacSAlex Lorenz                                             cl::cat(ClangDiffCategory));
52a75b2cacSAlex Lorenz 
53849f20e4SJohannes Altmanninger static cl::opt<int> MaxSize("s", cl::desc("<maxsize>"), cl::Optional,
54849f20e4SJohannes Altmanninger                             cl::init(-1), cl::cat(ClangDiffCategory));
55849f20e4SJohannes Altmanninger 
56849f20e4SJohannes Altmanninger static cl::opt<std::string> BuildPath("p", cl::desc("Build path"), cl::init(""),
57849f20e4SJohannes Altmanninger                                       cl::Optional, cl::cat(ClangDiffCategory));
58849f20e4SJohannes Altmanninger 
59849f20e4SJohannes Altmanninger static cl::list<std::string> ArgsAfter(
60849f20e4SJohannes Altmanninger     "extra-arg",
61849f20e4SJohannes Altmanninger     cl::desc("Additional argument to append to the compiler command line"),
62849f20e4SJohannes Altmanninger     cl::cat(ClangDiffCategory));
63849f20e4SJohannes Altmanninger 
64849f20e4SJohannes Altmanninger static cl::list<std::string> ArgsBefore(
65849f20e4SJohannes Altmanninger     "extra-arg-before",
66849f20e4SJohannes Altmanninger     cl::desc("Additional argument to prepend to the compiler command line"),
67849f20e4SJohannes Altmanninger     cl::cat(ClangDiffCategory));
68849f20e4SJohannes Altmanninger 
69849f20e4SJohannes Altmanninger static void addExtraArgs(std::unique_ptr<CompilationDatabase> &Compilations) {
70849f20e4SJohannes Altmanninger   if (!Compilations)
71849f20e4SJohannes Altmanninger     return;
72849f20e4SJohannes Altmanninger   auto AdjustingCompilations =
73849f20e4SJohannes Altmanninger       llvm::make_unique<ArgumentsAdjustingCompilations>(
74849f20e4SJohannes Altmanninger           std::move(Compilations));
75849f20e4SJohannes Altmanninger   AdjustingCompilations->appendArgumentsAdjuster(
76849f20e4SJohannes Altmanninger       getInsertArgumentAdjuster(ArgsBefore, ArgumentInsertPosition::BEGIN));
77849f20e4SJohannes Altmanninger   AdjustingCompilations->appendArgumentsAdjuster(
78849f20e4SJohannes Altmanninger       getInsertArgumentAdjuster(ArgsAfter, ArgumentInsertPosition::END));
79849f20e4SJohannes Altmanninger   Compilations = std::move(AdjustingCompilations);
80849f20e4SJohannes Altmanninger }
81849f20e4SJohannes Altmanninger 
82849f20e4SJohannes Altmanninger static std::unique_ptr<ASTUnit>
83849f20e4SJohannes Altmanninger getAST(const std::unique_ptr<CompilationDatabase> &CommonCompilations,
84849f20e4SJohannes Altmanninger        const StringRef Filename) {
85a75b2cacSAlex Lorenz   std::string ErrorMessage;
86a75b2cacSAlex Lorenz   std::unique_ptr<CompilationDatabase> Compilations;
87849f20e4SJohannes Altmanninger   if (!CommonCompilations) {
88849f20e4SJohannes Altmanninger     Compilations = CompilationDatabase::autoDetectFromSource(
89849f20e4SJohannes Altmanninger         BuildPath.empty() ? Filename : BuildPath, ErrorMessage);
90a75b2cacSAlex Lorenz     if (!Compilations) {
91a75b2cacSAlex Lorenz       llvm::errs()
92a75b2cacSAlex Lorenz           << "Error while trying to load a compilation database, running "
93a75b2cacSAlex Lorenz              "without flags.\n"
94a75b2cacSAlex Lorenz           << ErrorMessage;
95849f20e4SJohannes Altmanninger       Compilations =
96849f20e4SJohannes Altmanninger           llvm::make_unique<clang::tooling::FixedCompilationDatabase>(
97a75b2cacSAlex Lorenz               ".", std::vector<std::string>());
98a75b2cacSAlex Lorenz     }
99849f20e4SJohannes Altmanninger   }
100849f20e4SJohannes Altmanninger   addExtraArgs(Compilations);
101a75b2cacSAlex Lorenz   std::array<std::string, 1> Files = {{Filename}};
102849f20e4SJohannes Altmanninger   ClangTool Tool(Compilations ? *Compilations : *CommonCompilations, Files);
103a75b2cacSAlex Lorenz   std::vector<std::unique_ptr<ASTUnit>> ASTs;
104a75b2cacSAlex Lorenz   Tool.buildASTs(ASTs);
105a75b2cacSAlex Lorenz   if (ASTs.size() != Files.size())
106a75b2cacSAlex Lorenz     return nullptr;
107a75b2cacSAlex Lorenz   return std::move(ASTs[0]);
108a75b2cacSAlex Lorenz }
109a75b2cacSAlex Lorenz 
1100da12c84SJohannes Altmanninger static char hexdigit(int N) { return N &= 0xf, N + (N < 10 ? '0' : 'a' - 10); }
1110da12c84SJohannes Altmanninger 
112*a29d6aecSJohannes Altmanninger static const char HtmlDiffHeader[] = R"(
113*a29d6aecSJohannes Altmanninger <html>
114*a29d6aecSJohannes Altmanninger <head>
115*a29d6aecSJohannes Altmanninger <meta charset='utf-8'/>
116*a29d6aecSJohannes Altmanninger <style>
117*a29d6aecSJohannes Altmanninger span.d { color: red; }
118*a29d6aecSJohannes Altmanninger span.u { color: #cc00cc; }
119*a29d6aecSJohannes Altmanninger span.i { color: green; }
120*a29d6aecSJohannes Altmanninger span.m { font-weight: bold; }
121*a29d6aecSJohannes Altmanninger span   { font-weight: normal; color: black; }
122*a29d6aecSJohannes Altmanninger div.code {
123*a29d6aecSJohannes Altmanninger   width: 48%;
124*a29d6aecSJohannes Altmanninger   height: 98%;
125*a29d6aecSJohannes Altmanninger   overflow: scroll;
126*a29d6aecSJohannes Altmanninger   float: left;
127*a29d6aecSJohannes Altmanninger   padding: 0 0 0.5% 0.5%;
128*a29d6aecSJohannes Altmanninger   border: solid 2px LightGrey;
129*a29d6aecSJohannes Altmanninger   border-radius: 5px;
130*a29d6aecSJohannes Altmanninger }
131*a29d6aecSJohannes Altmanninger </style>
132*a29d6aecSJohannes Altmanninger </head>
133*a29d6aecSJohannes Altmanninger <script type='text/javascript'>
134*a29d6aecSJohannes Altmanninger highlightStack = []
135*a29d6aecSJohannes Altmanninger function clearHighlight() {
136*a29d6aecSJohannes Altmanninger   while (highlightStack.length) {
137*a29d6aecSJohannes Altmanninger     let [l, r] = highlightStack.pop()
138*a29d6aecSJohannes Altmanninger     document.getElementById(l).style.backgroundColor = 'white'
139*a29d6aecSJohannes Altmanninger     document.getElementById(r).style.backgroundColor = 'white'
140*a29d6aecSJohannes Altmanninger   }
141*a29d6aecSJohannes Altmanninger }
142*a29d6aecSJohannes Altmanninger function highlight(event) {
143*a29d6aecSJohannes Altmanninger   id = event.target['id']
144*a29d6aecSJohannes Altmanninger   doHighlight(id)
145*a29d6aecSJohannes Altmanninger }
146*a29d6aecSJohannes Altmanninger function doHighlight(id) {
147*a29d6aecSJohannes Altmanninger   clearHighlight()
148*a29d6aecSJohannes Altmanninger   source = document.getElementById(id)
149*a29d6aecSJohannes Altmanninger   if (!source.attributes['tid'])
150*a29d6aecSJohannes Altmanninger     return
151*a29d6aecSJohannes Altmanninger   tid = source.attributes['tid'].value
152*a29d6aecSJohannes Altmanninger   target = document.getElementById(tid)
153*a29d6aecSJohannes Altmanninger   if (!target || source.parentElement && source.parentElement.classList.contains('code'))
154*a29d6aecSJohannes Altmanninger     return
155*a29d6aecSJohannes Altmanninger   source.style.backgroundColor = target.style.backgroundColor = 'lightgrey'
156*a29d6aecSJohannes Altmanninger   highlightStack.push([id, tid])
157*a29d6aecSJohannes Altmanninger   source.scrollIntoView()
158*a29d6aecSJohannes Altmanninger   target.scrollIntoView()
159*a29d6aecSJohannes Altmanninger   location.hash = '#' + id
160*a29d6aecSJohannes Altmanninger }
161*a29d6aecSJohannes Altmanninger function scrollToBoth() {
162*a29d6aecSJohannes Altmanninger   doHighlight(location.hash.substr(1))
163*a29d6aecSJohannes Altmanninger }
164*a29d6aecSJohannes Altmanninger window.onload = scrollToBoth
165*a29d6aecSJohannes Altmanninger </script>
166*a29d6aecSJohannes Altmanninger <body>
167*a29d6aecSJohannes Altmanninger <div onclick='highlight(event)'>
168*a29d6aecSJohannes Altmanninger )";
169*a29d6aecSJohannes Altmanninger 
170*a29d6aecSJohannes Altmanninger static void printHtml(raw_ostream &OS, char C) {
171*a29d6aecSJohannes Altmanninger   switch (C) {
172*a29d6aecSJohannes Altmanninger   case '&':
173*a29d6aecSJohannes Altmanninger     OS << "&amp;";
174*a29d6aecSJohannes Altmanninger     break;
175*a29d6aecSJohannes Altmanninger   case '<':
176*a29d6aecSJohannes Altmanninger     OS << "&lt;";
177*a29d6aecSJohannes Altmanninger     break;
178*a29d6aecSJohannes Altmanninger   case '>':
179*a29d6aecSJohannes Altmanninger     OS << "&gt;";
180*a29d6aecSJohannes Altmanninger     break;
181*a29d6aecSJohannes Altmanninger   case '\'':
182*a29d6aecSJohannes Altmanninger     OS << "&#x27;";
183*a29d6aecSJohannes Altmanninger     break;
184*a29d6aecSJohannes Altmanninger   case '"':
185*a29d6aecSJohannes Altmanninger     OS << "&quot;";
186*a29d6aecSJohannes Altmanninger     break;
187*a29d6aecSJohannes Altmanninger   default:
188*a29d6aecSJohannes Altmanninger     OS << C;
189*a29d6aecSJohannes Altmanninger   }
190*a29d6aecSJohannes Altmanninger }
191*a29d6aecSJohannes Altmanninger 
192*a29d6aecSJohannes Altmanninger static void printHtml(raw_ostream &OS, const StringRef Str) {
193*a29d6aecSJohannes Altmanninger   for (char C : Str)
194*a29d6aecSJohannes Altmanninger     printHtml(OS, C);
195*a29d6aecSJohannes Altmanninger }
196*a29d6aecSJohannes Altmanninger 
197*a29d6aecSJohannes Altmanninger static std::string getChangeKindAbbr(diff::ChangeKind Kind) {
198*a29d6aecSJohannes Altmanninger   switch (Kind) {
199*a29d6aecSJohannes Altmanninger   case diff::None:
200*a29d6aecSJohannes Altmanninger     return "";
201*a29d6aecSJohannes Altmanninger   case diff::Delete:
202*a29d6aecSJohannes Altmanninger     return "d";
203*a29d6aecSJohannes Altmanninger   case diff::Update:
204*a29d6aecSJohannes Altmanninger     return "u";
205*a29d6aecSJohannes Altmanninger   case diff::Insert:
206*a29d6aecSJohannes Altmanninger     return "i";
207*a29d6aecSJohannes Altmanninger   case diff::Move:
208*a29d6aecSJohannes Altmanninger     return "m";
209*a29d6aecSJohannes Altmanninger   case diff::UpdateMove:
210*a29d6aecSJohannes Altmanninger     return "u m";
211*a29d6aecSJohannes Altmanninger   }
212*a29d6aecSJohannes Altmanninger }
213*a29d6aecSJohannes Altmanninger 
214*a29d6aecSJohannes Altmanninger static unsigned printHtmlForNode(raw_ostream &OS, const diff::ASTDiff &Diff,
215*a29d6aecSJohannes Altmanninger                                  diff::SyntaxTree &Tree, bool IsLeft,
216*a29d6aecSJohannes Altmanninger                                  diff::NodeId Id, unsigned Offset) {
217*a29d6aecSJohannes Altmanninger   const diff::Node &Node = Tree.getNode(Id);
218*a29d6aecSJohannes Altmanninger   char MyTag, OtherTag;
219*a29d6aecSJohannes Altmanninger   diff::NodeId LeftId, RightId;
220*a29d6aecSJohannes Altmanninger   diff::NodeId TargetId = Diff.getMapped(Tree, Id);
221*a29d6aecSJohannes Altmanninger   if (IsLeft) {
222*a29d6aecSJohannes Altmanninger     MyTag = 'L';
223*a29d6aecSJohannes Altmanninger     OtherTag = 'R';
224*a29d6aecSJohannes Altmanninger     LeftId = Id;
225*a29d6aecSJohannes Altmanninger     RightId = TargetId;
226*a29d6aecSJohannes Altmanninger   } else {
227*a29d6aecSJohannes Altmanninger     MyTag = 'R';
228*a29d6aecSJohannes Altmanninger     OtherTag = 'L';
229*a29d6aecSJohannes Altmanninger     LeftId = TargetId;
230*a29d6aecSJohannes Altmanninger     RightId = Id;
231*a29d6aecSJohannes Altmanninger   }
232*a29d6aecSJohannes Altmanninger   unsigned Begin, End;
233*a29d6aecSJohannes Altmanninger   std::tie(Begin, End) = Tree.getSourceRangeOffsets(Node);
234*a29d6aecSJohannes Altmanninger   const SourceManager &SrcMgr = Tree.getASTContext().getSourceManager();
235*a29d6aecSJohannes Altmanninger   auto Code = SrcMgr.getBuffer(SrcMgr.getMainFileID())->getBuffer();
236*a29d6aecSJohannes Altmanninger   for (; Offset < Begin; ++Offset)
237*a29d6aecSJohannes Altmanninger     printHtml(OS, Code[Offset]);
238*a29d6aecSJohannes Altmanninger   OS << "<span id='" << MyTag << Id << "' "
239*a29d6aecSJohannes Altmanninger      << "tid='" << OtherTag << TargetId << "' ";
240*a29d6aecSJohannes Altmanninger   OS << "title='";
241*a29d6aecSJohannes Altmanninger   printHtml(OS, Node.getTypeLabel());
242*a29d6aecSJohannes Altmanninger   OS << "\n" << LeftId << " -> " << RightId;
243*a29d6aecSJohannes Altmanninger   std::string Value = Tree.getNodeValue(Node);
244*a29d6aecSJohannes Altmanninger   if (!Value.empty()) {
245*a29d6aecSJohannes Altmanninger     OS << "\n";
246*a29d6aecSJohannes Altmanninger     printHtml(OS, Value);
247*a29d6aecSJohannes Altmanninger   }
248*a29d6aecSJohannes Altmanninger   OS << "'";
249*a29d6aecSJohannes Altmanninger   if (Node.Change != diff::None)
250*a29d6aecSJohannes Altmanninger     OS << " class='" << getChangeKindAbbr(Node.Change) << "'";
251*a29d6aecSJohannes Altmanninger   OS << ">";
252*a29d6aecSJohannes Altmanninger 
253*a29d6aecSJohannes Altmanninger   for (diff::NodeId Child : Node.Children)
254*a29d6aecSJohannes Altmanninger     Offset = printHtmlForNode(OS, Diff, Tree, IsLeft, Child, Offset);
255*a29d6aecSJohannes Altmanninger 
256*a29d6aecSJohannes Altmanninger   for (; Offset < End; ++Offset)
257*a29d6aecSJohannes Altmanninger     printHtml(OS, Code[Offset]);
258*a29d6aecSJohannes Altmanninger   if (Id == Tree.getRootId()) {
259*a29d6aecSJohannes Altmanninger     End = Code.size();
260*a29d6aecSJohannes Altmanninger     for (; Offset < End; ++Offset)
261*a29d6aecSJohannes Altmanninger       printHtml(OS, Code[Offset]);
262*a29d6aecSJohannes Altmanninger   }
263*a29d6aecSJohannes Altmanninger   OS << "</span>";
264*a29d6aecSJohannes Altmanninger   return Offset;
265*a29d6aecSJohannes Altmanninger }
266*a29d6aecSJohannes Altmanninger 
2670da12c84SJohannes Altmanninger static void printJsonString(raw_ostream &OS, const StringRef Str) {
26814be1839SJohannes Altmanninger   for (signed char C : Str) {
2690da12c84SJohannes Altmanninger     switch (C) {
2700da12c84SJohannes Altmanninger     case '"':
2710da12c84SJohannes Altmanninger       OS << R"(\")";
2720da12c84SJohannes Altmanninger       break;
2730da12c84SJohannes Altmanninger     case '\\':
2740da12c84SJohannes Altmanninger       OS << R"(\\)";
2750da12c84SJohannes Altmanninger       break;
2760da12c84SJohannes Altmanninger     case '\n':
2770da12c84SJohannes Altmanninger       OS << R"(\n)";
2780da12c84SJohannes Altmanninger       break;
2790da12c84SJohannes Altmanninger     case '\t':
2800da12c84SJohannes Altmanninger       OS << R"(\t)";
2810da12c84SJohannes Altmanninger       break;
2820da12c84SJohannes Altmanninger     default:
2830da12c84SJohannes Altmanninger       if ('\x00' <= C && C <= '\x1f') {
2840da12c84SJohannes Altmanninger         OS << R"(\u00)" << hexdigit(C >> 4) << hexdigit(C);
2850da12c84SJohannes Altmanninger       } else {
2860da12c84SJohannes Altmanninger         OS << C;
2870da12c84SJohannes Altmanninger       }
2880da12c84SJohannes Altmanninger     }
2890da12c84SJohannes Altmanninger   }
2900da12c84SJohannes Altmanninger }
2910da12c84SJohannes Altmanninger 
2920da12c84SJohannes Altmanninger static void printNodeAttributes(raw_ostream &OS, diff::SyntaxTree &Tree,
2930da12c84SJohannes Altmanninger                                 diff::NodeId Id) {
2940da12c84SJohannes Altmanninger   const diff::Node &N = Tree.getNode(Id);
2950da12c84SJohannes Altmanninger   OS << R"("id":)" << int(Id);
2960da12c84SJohannes Altmanninger   OS << R"(,"type":")" << N.getTypeLabel() << '"';
2970da12c84SJohannes Altmanninger   auto Offsets = Tree.getSourceRangeOffsets(N);
2980da12c84SJohannes Altmanninger   OS << R"(,"begin":)" << Offsets.first;
2990da12c84SJohannes Altmanninger   OS << R"(,"end":)" << Offsets.second;
300e0fe5cd4SJohannes Altmanninger   std::string Value = Tree.getNodeValue(N);
3010da12c84SJohannes Altmanninger   if (!Value.empty()) {
3020da12c84SJohannes Altmanninger     OS << R"(,"value":")";
3030da12c84SJohannes Altmanninger     printJsonString(OS, Value);
3040da12c84SJohannes Altmanninger     OS << '"';
3050da12c84SJohannes Altmanninger   }
3060da12c84SJohannes Altmanninger }
3070da12c84SJohannes Altmanninger 
3080da12c84SJohannes Altmanninger static void printNodeAsJson(raw_ostream &OS, diff::SyntaxTree &Tree,
3090da12c84SJohannes Altmanninger                             diff::NodeId Id) {
3100da12c84SJohannes Altmanninger   const diff::Node &N = Tree.getNode(Id);
3110da12c84SJohannes Altmanninger   OS << "{";
3120da12c84SJohannes Altmanninger   printNodeAttributes(OS, Tree, Id);
3130da12c84SJohannes Altmanninger   OS << R"(,"children":[)";
3140da12c84SJohannes Altmanninger   if (N.Children.size() > 0) {
3150da12c84SJohannes Altmanninger     printNodeAsJson(OS, Tree, N.Children[0]);
3160da12c84SJohannes Altmanninger     for (size_t I = 1, E = N.Children.size(); I < E; ++I) {
3170da12c84SJohannes Altmanninger       OS << ",";
3180da12c84SJohannes Altmanninger       printNodeAsJson(OS, Tree, N.Children[I]);
3190da12c84SJohannes Altmanninger     }
3200da12c84SJohannes Altmanninger   }
3210da12c84SJohannes Altmanninger   OS << "]}";
3220da12c84SJohannes Altmanninger }
3230da12c84SJohannes Altmanninger 
324e0fe5cd4SJohannes Altmanninger static void printNode(raw_ostream &OS, diff::SyntaxTree &Tree,
325e0fe5cd4SJohannes Altmanninger                       diff::NodeId Id) {
326e0fe5cd4SJohannes Altmanninger   if (Id.isInvalid()) {
327e0fe5cd4SJohannes Altmanninger     OS << "None";
328e0fe5cd4SJohannes Altmanninger     return;
329e0fe5cd4SJohannes Altmanninger   }
330e0fe5cd4SJohannes Altmanninger   OS << Tree.getNode(Id).getTypeLabel();
331e0fe5cd4SJohannes Altmanninger   std::string Value = Tree.getNodeValue(Id);
332e0fe5cd4SJohannes Altmanninger   if (!Value.empty())
333e0fe5cd4SJohannes Altmanninger     OS << ": " << Value;
334e0fe5cd4SJohannes Altmanninger   OS << "(" << Id << ")";
335e0fe5cd4SJohannes Altmanninger }
336e0fe5cd4SJohannes Altmanninger 
337a1d2b5d5SJohannes Altmanninger static void printTree(raw_ostream &OS, diff::SyntaxTree &Tree) {
338a1d2b5d5SJohannes Altmanninger   for (diff::NodeId Id : Tree) {
339a1d2b5d5SJohannes Altmanninger     for (int I = 0; I < Tree.getNode(Id).Depth; ++I)
340a1d2b5d5SJohannes Altmanninger       OS << " ";
341a1d2b5d5SJohannes Altmanninger     printNode(OS, Tree, Id);
342a1d2b5d5SJohannes Altmanninger     OS << "\n";
343a1d2b5d5SJohannes Altmanninger   }
344a1d2b5d5SJohannes Altmanninger }
345a1d2b5d5SJohannes Altmanninger 
346e0fe5cd4SJohannes Altmanninger static void printDstChange(raw_ostream &OS, diff::ASTDiff &Diff,
347e0fe5cd4SJohannes Altmanninger                            diff::SyntaxTree &SrcTree, diff::SyntaxTree &DstTree,
348e0fe5cd4SJohannes Altmanninger                            diff::NodeId Dst) {
349e0fe5cd4SJohannes Altmanninger   const diff::Node &DstNode = DstTree.getNode(Dst);
350e0fe5cd4SJohannes Altmanninger   diff::NodeId Src = Diff.getMapped(DstTree, Dst);
351e0fe5cd4SJohannes Altmanninger   switch (DstNode.Change) {
352e0fe5cd4SJohannes Altmanninger   case diff::None:
353e0fe5cd4SJohannes Altmanninger     break;
354e0fe5cd4SJohannes Altmanninger   case diff::Delete:
355e0fe5cd4SJohannes Altmanninger     llvm_unreachable("The destination tree can't have deletions.");
356e0fe5cd4SJohannes Altmanninger   case diff::Update:
357e0fe5cd4SJohannes Altmanninger     OS << "Update ";
358e0fe5cd4SJohannes Altmanninger     printNode(OS, SrcTree, Src);
359e0fe5cd4SJohannes Altmanninger     OS << " to " << DstTree.getNodeValue(Dst) << "\n";
360e0fe5cd4SJohannes Altmanninger     break;
361e0fe5cd4SJohannes Altmanninger   case diff::Insert:
362e0fe5cd4SJohannes Altmanninger   case diff::Move:
363e0fe5cd4SJohannes Altmanninger   case diff::UpdateMove:
364e0fe5cd4SJohannes Altmanninger     if (DstNode.Change == diff::Insert)
365e0fe5cd4SJohannes Altmanninger       OS << "Insert";
366e0fe5cd4SJohannes Altmanninger     else if (DstNode.Change == diff::Move)
367e0fe5cd4SJohannes Altmanninger       OS << "Move";
368e0fe5cd4SJohannes Altmanninger     else if (DstNode.Change == diff::UpdateMove)
369e0fe5cd4SJohannes Altmanninger       OS << "Update and Move";
370e0fe5cd4SJohannes Altmanninger     OS << " ";
371e0fe5cd4SJohannes Altmanninger     printNode(OS, DstTree, Dst);
372e0fe5cd4SJohannes Altmanninger     OS << " into ";
373e0fe5cd4SJohannes Altmanninger     printNode(OS, DstTree, DstNode.Parent);
374e0fe5cd4SJohannes Altmanninger     OS << " at " << DstTree.findPositionInParent(Dst) << "\n";
375e0fe5cd4SJohannes Altmanninger     break;
376e0fe5cd4SJohannes Altmanninger   }
377e0fe5cd4SJohannes Altmanninger }
378e0fe5cd4SJohannes Altmanninger 
379a75b2cacSAlex Lorenz int main(int argc, const char **argv) {
380849f20e4SJohannes Altmanninger   std::string ErrorMessage;
381849f20e4SJohannes Altmanninger   std::unique_ptr<CompilationDatabase> CommonCompilations =
382849f20e4SJohannes Altmanninger       FixedCompilationDatabase::loadFromCommandLine(argc, argv, ErrorMessage);
383849f20e4SJohannes Altmanninger   if (!CommonCompilations && !ErrorMessage.empty())
384849f20e4SJohannes Altmanninger     llvm::errs() << ErrorMessage;
385a75b2cacSAlex Lorenz   cl::HideUnrelatedOptions(ClangDiffCategory);
386a75b2cacSAlex Lorenz   if (!cl::ParseCommandLineOptions(argc, argv)) {
387a75b2cacSAlex Lorenz     cl::PrintOptionValues();
388a75b2cacSAlex Lorenz     return 1;
389a75b2cacSAlex Lorenz   }
390a75b2cacSAlex Lorenz 
391849f20e4SJohannes Altmanninger   addExtraArgs(CommonCompilations);
392849f20e4SJohannes Altmanninger 
393a1d2b5d5SJohannes Altmanninger   if (ASTDump || ASTDumpJson) {
394a75b2cacSAlex Lorenz     if (!DestinationPath.empty()) {
395a75b2cacSAlex Lorenz       llvm::errs() << "Error: Please specify exactly one filename.\n";
396a75b2cacSAlex Lorenz       return 1;
397a75b2cacSAlex Lorenz     }
398849f20e4SJohannes Altmanninger     std::unique_ptr<ASTUnit> AST = getAST(CommonCompilations, SourcePath);
399a75b2cacSAlex Lorenz     if (!AST)
400a75b2cacSAlex Lorenz       return 1;
401a75b2cacSAlex Lorenz     diff::SyntaxTree Tree(AST->getASTContext());
402a1d2b5d5SJohannes Altmanninger     if (ASTDump) {
403a1d2b5d5SJohannes Altmanninger       printTree(llvm::outs(), Tree);
404a1d2b5d5SJohannes Altmanninger       return 0;
405a1d2b5d5SJohannes Altmanninger     }
4060da12c84SJohannes Altmanninger     llvm::outs() << R"({"filename":")";
4070da12c84SJohannes Altmanninger     printJsonString(llvm::outs(), SourcePath);
4080da12c84SJohannes Altmanninger     llvm::outs() << R"(","root":)";
4090da12c84SJohannes Altmanninger     printNodeAsJson(llvm::outs(), Tree, Tree.getRootId());
4100da12c84SJohannes Altmanninger     llvm::outs() << "}\n";
411a75b2cacSAlex Lorenz     return 0;
412a75b2cacSAlex Lorenz   }
413a75b2cacSAlex Lorenz 
414a75b2cacSAlex Lorenz   if (DestinationPath.empty()) {
415a75b2cacSAlex Lorenz     llvm::errs() << "Error: Exactly two paths are required.\n";
416a75b2cacSAlex Lorenz     return 1;
417a75b2cacSAlex Lorenz   }
418a75b2cacSAlex Lorenz 
419849f20e4SJohannes Altmanninger   std::unique_ptr<ASTUnit> Src = getAST(CommonCompilations, SourcePath);
420849f20e4SJohannes Altmanninger   std::unique_ptr<ASTUnit> Dst = getAST(CommonCompilations, DestinationPath);
421a75b2cacSAlex Lorenz   if (!Src || !Dst)
422a75b2cacSAlex Lorenz     return 1;
423a75b2cacSAlex Lorenz 
424a75b2cacSAlex Lorenz   diff::ComparisonOptions Options;
425849f20e4SJohannes Altmanninger   if (MaxSize != -1)
426849f20e4SJohannes Altmanninger     Options.MaxSize = MaxSize;
427a75b2cacSAlex Lorenz   diff::SyntaxTree SrcTree(Src->getASTContext());
428a75b2cacSAlex Lorenz   diff::SyntaxTree DstTree(Dst->getASTContext());
429e0fe5cd4SJohannes Altmanninger   diff::ASTDiff Diff(SrcTree, DstTree, Options);
430e0fe5cd4SJohannes Altmanninger 
431*a29d6aecSJohannes Altmanninger   if (HtmlDiff) {
432*a29d6aecSJohannes Altmanninger     llvm::outs() << HtmlDiffHeader << "<pre>";
433*a29d6aecSJohannes Altmanninger     llvm::outs() << "<div id='L' class='code'>";
434*a29d6aecSJohannes Altmanninger     printHtmlForNode(llvm::outs(), Diff, SrcTree, true, SrcTree.getRootId(), 0);
435*a29d6aecSJohannes Altmanninger     llvm::outs() << "</div>";
436*a29d6aecSJohannes Altmanninger     llvm::outs() << "<div id='R' class='code'>";
437*a29d6aecSJohannes Altmanninger     printHtmlForNode(llvm::outs(), Diff, DstTree, false, DstTree.getRootId(),
438*a29d6aecSJohannes Altmanninger                      0);
439*a29d6aecSJohannes Altmanninger     llvm::outs() << "</div>";
440*a29d6aecSJohannes Altmanninger     llvm::outs() << "</pre></div></body></html>\n";
441*a29d6aecSJohannes Altmanninger     return 0;
442*a29d6aecSJohannes Altmanninger   }
443*a29d6aecSJohannes Altmanninger 
444e0fe5cd4SJohannes Altmanninger   for (diff::NodeId Dst : DstTree) {
445e0fe5cd4SJohannes Altmanninger     diff::NodeId Src = Diff.getMapped(DstTree, Dst);
446683876caSJohannes Altmanninger     if (PrintMatches && Src.isValid()) {
447e0fe5cd4SJohannes Altmanninger       llvm::outs() << "Match ";
448e0fe5cd4SJohannes Altmanninger       printNode(llvm::outs(), SrcTree, Src);
449e0fe5cd4SJohannes Altmanninger       llvm::outs() << " to ";
450e0fe5cd4SJohannes Altmanninger       printNode(llvm::outs(), DstTree, Dst);
451e0fe5cd4SJohannes Altmanninger       llvm::outs() << "\n";
452e0fe5cd4SJohannes Altmanninger     }
453e0fe5cd4SJohannes Altmanninger     printDstChange(llvm::outs(), Diff, SrcTree, DstTree, Dst);
454e0fe5cd4SJohannes Altmanninger   }
455e0fe5cd4SJohannes Altmanninger   for (diff::NodeId Src : SrcTree) {
456e0fe5cd4SJohannes Altmanninger     if (Diff.getMapped(SrcTree, Src).isInvalid()) {
457e0fe5cd4SJohannes Altmanninger       llvm::outs() << "Delete ";
458e0fe5cd4SJohannes Altmanninger       printNode(llvm::outs(), SrcTree, Src);
459e0fe5cd4SJohannes Altmanninger       llvm::outs() << "\n";
460e0fe5cd4SJohannes Altmanninger     }
461e0fe5cd4SJohannes Altmanninger   }
462a75b2cacSAlex Lorenz 
463a75b2cacSAlex Lorenz   return 0;
464a75b2cacSAlex Lorenz }
465