xref: /llvm-project/clang/tools/clang-diff/ClangDiff.cpp (revision d196930799224f906ba4c5d3c64bafa1cad8d156)
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 
40a29d6aecSJohannes Altmanninger static cl::opt<bool> HtmlDiff("html",
41a29d6aecSJohannes Altmanninger                               cl::desc("Output a side-by-side diff in HTML."),
42a29d6aecSJohannes Altmanninger                               cl::init(false), cl::cat(ClangDiffCategory));
43a29d6aecSJohannes 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 
53*d1969307SJohannes Altmanninger static cl::opt<std::string> StopAfter("stop-after",
54*d1969307SJohannes Altmanninger                                       cl::desc("<topdown|bottomup>"),
55*d1969307SJohannes Altmanninger                                       cl::Optional, cl::init(""),
56*d1969307SJohannes Altmanninger                                       cl::cat(ClangDiffCategory));
57*d1969307SJohannes Altmanninger 
58849f20e4SJohannes Altmanninger static cl::opt<int> MaxSize("s", cl::desc("<maxsize>"), cl::Optional,
59849f20e4SJohannes Altmanninger                             cl::init(-1), cl::cat(ClangDiffCategory));
60849f20e4SJohannes Altmanninger 
61849f20e4SJohannes Altmanninger static cl::opt<std::string> BuildPath("p", cl::desc("Build path"), cl::init(""),
62849f20e4SJohannes Altmanninger                                       cl::Optional, cl::cat(ClangDiffCategory));
63849f20e4SJohannes Altmanninger 
64849f20e4SJohannes Altmanninger static cl::list<std::string> ArgsAfter(
65849f20e4SJohannes Altmanninger     "extra-arg",
66849f20e4SJohannes Altmanninger     cl::desc("Additional argument to append to the compiler command line"),
67849f20e4SJohannes Altmanninger     cl::cat(ClangDiffCategory));
68849f20e4SJohannes Altmanninger 
69849f20e4SJohannes Altmanninger static cl::list<std::string> ArgsBefore(
70849f20e4SJohannes Altmanninger     "extra-arg-before",
71849f20e4SJohannes Altmanninger     cl::desc("Additional argument to prepend to the compiler command line"),
72849f20e4SJohannes Altmanninger     cl::cat(ClangDiffCategory));
73849f20e4SJohannes Altmanninger 
74849f20e4SJohannes Altmanninger static void addExtraArgs(std::unique_ptr<CompilationDatabase> &Compilations) {
75849f20e4SJohannes Altmanninger   if (!Compilations)
76849f20e4SJohannes Altmanninger     return;
77849f20e4SJohannes Altmanninger   auto AdjustingCompilations =
78849f20e4SJohannes Altmanninger       llvm::make_unique<ArgumentsAdjustingCompilations>(
79849f20e4SJohannes Altmanninger           std::move(Compilations));
80849f20e4SJohannes Altmanninger   AdjustingCompilations->appendArgumentsAdjuster(
81849f20e4SJohannes Altmanninger       getInsertArgumentAdjuster(ArgsBefore, ArgumentInsertPosition::BEGIN));
82849f20e4SJohannes Altmanninger   AdjustingCompilations->appendArgumentsAdjuster(
83849f20e4SJohannes Altmanninger       getInsertArgumentAdjuster(ArgsAfter, ArgumentInsertPosition::END));
84849f20e4SJohannes Altmanninger   Compilations = std::move(AdjustingCompilations);
85849f20e4SJohannes Altmanninger }
86849f20e4SJohannes Altmanninger 
87849f20e4SJohannes Altmanninger static std::unique_ptr<ASTUnit>
88849f20e4SJohannes Altmanninger getAST(const std::unique_ptr<CompilationDatabase> &CommonCompilations,
89849f20e4SJohannes Altmanninger        const StringRef Filename) {
90a75b2cacSAlex Lorenz   std::string ErrorMessage;
91a75b2cacSAlex Lorenz   std::unique_ptr<CompilationDatabase> Compilations;
92849f20e4SJohannes Altmanninger   if (!CommonCompilations) {
93849f20e4SJohannes Altmanninger     Compilations = CompilationDatabase::autoDetectFromSource(
94849f20e4SJohannes Altmanninger         BuildPath.empty() ? Filename : BuildPath, ErrorMessage);
95a75b2cacSAlex Lorenz     if (!Compilations) {
96a75b2cacSAlex Lorenz       llvm::errs()
97a75b2cacSAlex Lorenz           << "Error while trying to load a compilation database, running "
98a75b2cacSAlex Lorenz              "without flags.\n"
99a75b2cacSAlex Lorenz           << ErrorMessage;
100849f20e4SJohannes Altmanninger       Compilations =
101849f20e4SJohannes Altmanninger           llvm::make_unique<clang::tooling::FixedCompilationDatabase>(
102a75b2cacSAlex Lorenz               ".", std::vector<std::string>());
103a75b2cacSAlex Lorenz     }
104849f20e4SJohannes Altmanninger   }
105849f20e4SJohannes Altmanninger   addExtraArgs(Compilations);
106a75b2cacSAlex Lorenz   std::array<std::string, 1> Files = {{Filename}};
107849f20e4SJohannes Altmanninger   ClangTool Tool(Compilations ? *Compilations : *CommonCompilations, Files);
108a75b2cacSAlex Lorenz   std::vector<std::unique_ptr<ASTUnit>> ASTs;
109a75b2cacSAlex Lorenz   Tool.buildASTs(ASTs);
110a75b2cacSAlex Lorenz   if (ASTs.size() != Files.size())
111a75b2cacSAlex Lorenz     return nullptr;
112a75b2cacSAlex Lorenz   return std::move(ASTs[0]);
113a75b2cacSAlex Lorenz }
114a75b2cacSAlex Lorenz 
1150da12c84SJohannes Altmanninger static char hexdigit(int N) { return N &= 0xf, N + (N < 10 ? '0' : 'a' - 10); }
1160da12c84SJohannes Altmanninger 
117a29d6aecSJohannes Altmanninger static const char HtmlDiffHeader[] = R"(
118a29d6aecSJohannes Altmanninger <html>
119a29d6aecSJohannes Altmanninger <head>
120a29d6aecSJohannes Altmanninger <meta charset='utf-8'/>
121a29d6aecSJohannes Altmanninger <style>
122a29d6aecSJohannes Altmanninger span.d { color: red; }
123a29d6aecSJohannes Altmanninger span.u { color: #cc00cc; }
124a29d6aecSJohannes Altmanninger span.i { color: green; }
125a29d6aecSJohannes Altmanninger span.m { font-weight: bold; }
126a29d6aecSJohannes Altmanninger span   { font-weight: normal; color: black; }
127a29d6aecSJohannes Altmanninger div.code {
128a29d6aecSJohannes Altmanninger   width: 48%;
129a29d6aecSJohannes Altmanninger   height: 98%;
130a29d6aecSJohannes Altmanninger   overflow: scroll;
131a29d6aecSJohannes Altmanninger   float: left;
132a29d6aecSJohannes Altmanninger   padding: 0 0 0.5% 0.5%;
133a29d6aecSJohannes Altmanninger   border: solid 2px LightGrey;
134a29d6aecSJohannes Altmanninger   border-radius: 5px;
135a29d6aecSJohannes Altmanninger }
136a29d6aecSJohannes Altmanninger </style>
137a29d6aecSJohannes Altmanninger </head>
138a29d6aecSJohannes Altmanninger <script type='text/javascript'>
139a29d6aecSJohannes Altmanninger highlightStack = []
140a29d6aecSJohannes Altmanninger function clearHighlight() {
141a29d6aecSJohannes Altmanninger   while (highlightStack.length) {
142a29d6aecSJohannes Altmanninger     let [l, r] = highlightStack.pop()
143a29d6aecSJohannes Altmanninger     document.getElementById(l).style.backgroundColor = 'white'
144a29d6aecSJohannes Altmanninger     document.getElementById(r).style.backgroundColor = 'white'
145a29d6aecSJohannes Altmanninger   }
146a29d6aecSJohannes Altmanninger }
147a29d6aecSJohannes Altmanninger function highlight(event) {
148a29d6aecSJohannes Altmanninger   id = event.target['id']
149a29d6aecSJohannes Altmanninger   doHighlight(id)
150a29d6aecSJohannes Altmanninger }
151a29d6aecSJohannes Altmanninger function doHighlight(id) {
152a29d6aecSJohannes Altmanninger   clearHighlight()
153a29d6aecSJohannes Altmanninger   source = document.getElementById(id)
154a29d6aecSJohannes Altmanninger   if (!source.attributes['tid'])
155a29d6aecSJohannes Altmanninger     return
156a29d6aecSJohannes Altmanninger   tid = source.attributes['tid'].value
157a29d6aecSJohannes Altmanninger   target = document.getElementById(tid)
158a29d6aecSJohannes Altmanninger   if (!target || source.parentElement && source.parentElement.classList.contains('code'))
159a29d6aecSJohannes Altmanninger     return
160a29d6aecSJohannes Altmanninger   source.style.backgroundColor = target.style.backgroundColor = 'lightgrey'
161a29d6aecSJohannes Altmanninger   highlightStack.push([id, tid])
162a29d6aecSJohannes Altmanninger   source.scrollIntoView()
163a29d6aecSJohannes Altmanninger   target.scrollIntoView()
164a29d6aecSJohannes Altmanninger   location.hash = '#' + id
165a29d6aecSJohannes Altmanninger }
166a29d6aecSJohannes Altmanninger function scrollToBoth() {
167a29d6aecSJohannes Altmanninger   doHighlight(location.hash.substr(1))
168a29d6aecSJohannes Altmanninger }
169a29d6aecSJohannes Altmanninger window.onload = scrollToBoth
170a29d6aecSJohannes Altmanninger </script>
171a29d6aecSJohannes Altmanninger <body>
172a29d6aecSJohannes Altmanninger <div onclick='highlight(event)'>
173a29d6aecSJohannes Altmanninger )";
174a29d6aecSJohannes Altmanninger 
175a29d6aecSJohannes Altmanninger static void printHtml(raw_ostream &OS, char C) {
176a29d6aecSJohannes Altmanninger   switch (C) {
177a29d6aecSJohannes Altmanninger   case '&':
178a29d6aecSJohannes Altmanninger     OS << "&amp;";
179a29d6aecSJohannes Altmanninger     break;
180a29d6aecSJohannes Altmanninger   case '<':
181a29d6aecSJohannes Altmanninger     OS << "&lt;";
182a29d6aecSJohannes Altmanninger     break;
183a29d6aecSJohannes Altmanninger   case '>':
184a29d6aecSJohannes Altmanninger     OS << "&gt;";
185a29d6aecSJohannes Altmanninger     break;
186a29d6aecSJohannes Altmanninger   case '\'':
187a29d6aecSJohannes Altmanninger     OS << "&#x27;";
188a29d6aecSJohannes Altmanninger     break;
189a29d6aecSJohannes Altmanninger   case '"':
190a29d6aecSJohannes Altmanninger     OS << "&quot;";
191a29d6aecSJohannes Altmanninger     break;
192a29d6aecSJohannes Altmanninger   default:
193a29d6aecSJohannes Altmanninger     OS << C;
194a29d6aecSJohannes Altmanninger   }
195a29d6aecSJohannes Altmanninger }
196a29d6aecSJohannes Altmanninger 
197a29d6aecSJohannes Altmanninger static void printHtml(raw_ostream &OS, const StringRef Str) {
198a29d6aecSJohannes Altmanninger   for (char C : Str)
199a29d6aecSJohannes Altmanninger     printHtml(OS, C);
200a29d6aecSJohannes Altmanninger }
201a29d6aecSJohannes Altmanninger 
202a29d6aecSJohannes Altmanninger static std::string getChangeKindAbbr(diff::ChangeKind Kind) {
203a29d6aecSJohannes Altmanninger   switch (Kind) {
204a29d6aecSJohannes Altmanninger   case diff::None:
205a29d6aecSJohannes Altmanninger     return "";
206a29d6aecSJohannes Altmanninger   case diff::Delete:
207a29d6aecSJohannes Altmanninger     return "d";
208a29d6aecSJohannes Altmanninger   case diff::Update:
209a29d6aecSJohannes Altmanninger     return "u";
210a29d6aecSJohannes Altmanninger   case diff::Insert:
211a29d6aecSJohannes Altmanninger     return "i";
212a29d6aecSJohannes Altmanninger   case diff::Move:
213a29d6aecSJohannes Altmanninger     return "m";
214a29d6aecSJohannes Altmanninger   case diff::UpdateMove:
215a29d6aecSJohannes Altmanninger     return "u m";
216a29d6aecSJohannes Altmanninger   }
217e1a89fbfSJohannes Altmanninger   llvm_unreachable("Invalid enumeration value.");
218a29d6aecSJohannes Altmanninger }
219a29d6aecSJohannes Altmanninger 
220a29d6aecSJohannes Altmanninger static unsigned printHtmlForNode(raw_ostream &OS, const diff::ASTDiff &Diff,
221a29d6aecSJohannes Altmanninger                                  diff::SyntaxTree &Tree, bool IsLeft,
222a29d6aecSJohannes Altmanninger                                  diff::NodeId Id, unsigned Offset) {
223a29d6aecSJohannes Altmanninger   const diff::Node &Node = Tree.getNode(Id);
224a29d6aecSJohannes Altmanninger   char MyTag, OtherTag;
225a29d6aecSJohannes Altmanninger   diff::NodeId LeftId, RightId;
226a29d6aecSJohannes Altmanninger   diff::NodeId TargetId = Diff.getMapped(Tree, Id);
227a29d6aecSJohannes Altmanninger   if (IsLeft) {
228a29d6aecSJohannes Altmanninger     MyTag = 'L';
229a29d6aecSJohannes Altmanninger     OtherTag = 'R';
230a29d6aecSJohannes Altmanninger     LeftId = Id;
231a29d6aecSJohannes Altmanninger     RightId = TargetId;
232a29d6aecSJohannes Altmanninger   } else {
233a29d6aecSJohannes Altmanninger     MyTag = 'R';
234a29d6aecSJohannes Altmanninger     OtherTag = 'L';
235a29d6aecSJohannes Altmanninger     LeftId = TargetId;
236a29d6aecSJohannes Altmanninger     RightId = Id;
237a29d6aecSJohannes Altmanninger   }
238a29d6aecSJohannes Altmanninger   unsigned Begin, End;
239a29d6aecSJohannes Altmanninger   std::tie(Begin, End) = Tree.getSourceRangeOffsets(Node);
240a29d6aecSJohannes Altmanninger   const SourceManager &SrcMgr = Tree.getASTContext().getSourceManager();
241a29d6aecSJohannes Altmanninger   auto Code = SrcMgr.getBuffer(SrcMgr.getMainFileID())->getBuffer();
242a29d6aecSJohannes Altmanninger   for (; Offset < Begin; ++Offset)
243a29d6aecSJohannes Altmanninger     printHtml(OS, Code[Offset]);
244a29d6aecSJohannes Altmanninger   OS << "<span id='" << MyTag << Id << "' "
245a29d6aecSJohannes Altmanninger      << "tid='" << OtherTag << TargetId << "' ";
246a29d6aecSJohannes Altmanninger   OS << "title='";
247a29d6aecSJohannes Altmanninger   printHtml(OS, Node.getTypeLabel());
248a29d6aecSJohannes Altmanninger   OS << "\n" << LeftId << " -> " << RightId;
249a29d6aecSJohannes Altmanninger   std::string Value = Tree.getNodeValue(Node);
250a29d6aecSJohannes Altmanninger   if (!Value.empty()) {
251a29d6aecSJohannes Altmanninger     OS << "\n";
252a29d6aecSJohannes Altmanninger     printHtml(OS, Value);
253a29d6aecSJohannes Altmanninger   }
254a29d6aecSJohannes Altmanninger   OS << "'";
255a29d6aecSJohannes Altmanninger   if (Node.Change != diff::None)
256a29d6aecSJohannes Altmanninger     OS << " class='" << getChangeKindAbbr(Node.Change) << "'";
257a29d6aecSJohannes Altmanninger   OS << ">";
258a29d6aecSJohannes Altmanninger 
259a29d6aecSJohannes Altmanninger   for (diff::NodeId Child : Node.Children)
260a29d6aecSJohannes Altmanninger     Offset = printHtmlForNode(OS, Diff, Tree, IsLeft, Child, Offset);
261a29d6aecSJohannes Altmanninger 
262a29d6aecSJohannes Altmanninger   for (; Offset < End; ++Offset)
263a29d6aecSJohannes Altmanninger     printHtml(OS, Code[Offset]);
264a29d6aecSJohannes Altmanninger   if (Id == Tree.getRootId()) {
265a29d6aecSJohannes Altmanninger     End = Code.size();
266a29d6aecSJohannes Altmanninger     for (; Offset < End; ++Offset)
267a29d6aecSJohannes Altmanninger       printHtml(OS, Code[Offset]);
268a29d6aecSJohannes Altmanninger   }
269a29d6aecSJohannes Altmanninger   OS << "</span>";
270a29d6aecSJohannes Altmanninger   return Offset;
271a29d6aecSJohannes Altmanninger }
272a29d6aecSJohannes Altmanninger 
2730da12c84SJohannes Altmanninger static void printJsonString(raw_ostream &OS, const StringRef Str) {
27414be1839SJohannes Altmanninger   for (signed char C : Str) {
2750da12c84SJohannes Altmanninger     switch (C) {
2760da12c84SJohannes Altmanninger     case '"':
2770da12c84SJohannes Altmanninger       OS << R"(\")";
2780da12c84SJohannes Altmanninger       break;
2790da12c84SJohannes Altmanninger     case '\\':
2800da12c84SJohannes Altmanninger       OS << R"(\\)";
2810da12c84SJohannes Altmanninger       break;
2820da12c84SJohannes Altmanninger     case '\n':
2830da12c84SJohannes Altmanninger       OS << R"(\n)";
2840da12c84SJohannes Altmanninger       break;
2850da12c84SJohannes Altmanninger     case '\t':
2860da12c84SJohannes Altmanninger       OS << R"(\t)";
2870da12c84SJohannes Altmanninger       break;
2880da12c84SJohannes Altmanninger     default:
2890da12c84SJohannes Altmanninger       if ('\x00' <= C && C <= '\x1f') {
2900da12c84SJohannes Altmanninger         OS << R"(\u00)" << hexdigit(C >> 4) << hexdigit(C);
2910da12c84SJohannes Altmanninger       } else {
2920da12c84SJohannes Altmanninger         OS << C;
2930da12c84SJohannes Altmanninger       }
2940da12c84SJohannes Altmanninger     }
2950da12c84SJohannes Altmanninger   }
2960da12c84SJohannes Altmanninger }
2970da12c84SJohannes Altmanninger 
2980da12c84SJohannes Altmanninger static void printNodeAttributes(raw_ostream &OS, diff::SyntaxTree &Tree,
2990da12c84SJohannes Altmanninger                                 diff::NodeId Id) {
3000da12c84SJohannes Altmanninger   const diff::Node &N = Tree.getNode(Id);
3010da12c84SJohannes Altmanninger   OS << R"("id":)" << int(Id);
3020da12c84SJohannes Altmanninger   OS << R"(,"type":")" << N.getTypeLabel() << '"';
3030da12c84SJohannes Altmanninger   auto Offsets = Tree.getSourceRangeOffsets(N);
3040da12c84SJohannes Altmanninger   OS << R"(,"begin":)" << Offsets.first;
3050da12c84SJohannes Altmanninger   OS << R"(,"end":)" << Offsets.second;
306e0fe5cd4SJohannes Altmanninger   std::string Value = Tree.getNodeValue(N);
3070da12c84SJohannes Altmanninger   if (!Value.empty()) {
3080da12c84SJohannes Altmanninger     OS << R"(,"value":")";
3090da12c84SJohannes Altmanninger     printJsonString(OS, Value);
3100da12c84SJohannes Altmanninger     OS << '"';
3110da12c84SJohannes Altmanninger   }
3120da12c84SJohannes Altmanninger }
3130da12c84SJohannes Altmanninger 
3140da12c84SJohannes Altmanninger static void printNodeAsJson(raw_ostream &OS, diff::SyntaxTree &Tree,
3150da12c84SJohannes Altmanninger                             diff::NodeId Id) {
3160da12c84SJohannes Altmanninger   const diff::Node &N = Tree.getNode(Id);
3170da12c84SJohannes Altmanninger   OS << "{";
3180da12c84SJohannes Altmanninger   printNodeAttributes(OS, Tree, Id);
3190da12c84SJohannes Altmanninger   OS << R"(,"children":[)";
3200da12c84SJohannes Altmanninger   if (N.Children.size() > 0) {
3210da12c84SJohannes Altmanninger     printNodeAsJson(OS, Tree, N.Children[0]);
3220da12c84SJohannes Altmanninger     for (size_t I = 1, E = N.Children.size(); I < E; ++I) {
3230da12c84SJohannes Altmanninger       OS << ",";
3240da12c84SJohannes Altmanninger       printNodeAsJson(OS, Tree, N.Children[I]);
3250da12c84SJohannes Altmanninger     }
3260da12c84SJohannes Altmanninger   }
3270da12c84SJohannes Altmanninger   OS << "]}";
3280da12c84SJohannes Altmanninger }
3290da12c84SJohannes Altmanninger 
330e0fe5cd4SJohannes Altmanninger static void printNode(raw_ostream &OS, diff::SyntaxTree &Tree,
331e0fe5cd4SJohannes Altmanninger                       diff::NodeId Id) {
332e0fe5cd4SJohannes Altmanninger   if (Id.isInvalid()) {
333e0fe5cd4SJohannes Altmanninger     OS << "None";
334e0fe5cd4SJohannes Altmanninger     return;
335e0fe5cd4SJohannes Altmanninger   }
336e0fe5cd4SJohannes Altmanninger   OS << Tree.getNode(Id).getTypeLabel();
337e0fe5cd4SJohannes Altmanninger   std::string Value = Tree.getNodeValue(Id);
338e0fe5cd4SJohannes Altmanninger   if (!Value.empty())
339e0fe5cd4SJohannes Altmanninger     OS << ": " << Value;
340e0fe5cd4SJohannes Altmanninger   OS << "(" << Id << ")";
341e0fe5cd4SJohannes Altmanninger }
342e0fe5cd4SJohannes Altmanninger 
343a1d2b5d5SJohannes Altmanninger static void printTree(raw_ostream &OS, diff::SyntaxTree &Tree) {
344a1d2b5d5SJohannes Altmanninger   for (diff::NodeId Id : Tree) {
345a1d2b5d5SJohannes Altmanninger     for (int I = 0; I < Tree.getNode(Id).Depth; ++I)
346a1d2b5d5SJohannes Altmanninger       OS << " ";
347a1d2b5d5SJohannes Altmanninger     printNode(OS, Tree, Id);
348a1d2b5d5SJohannes Altmanninger     OS << "\n";
349a1d2b5d5SJohannes Altmanninger   }
350a1d2b5d5SJohannes Altmanninger }
351a1d2b5d5SJohannes Altmanninger 
352e0fe5cd4SJohannes Altmanninger static void printDstChange(raw_ostream &OS, diff::ASTDiff &Diff,
353e0fe5cd4SJohannes Altmanninger                            diff::SyntaxTree &SrcTree, diff::SyntaxTree &DstTree,
354e0fe5cd4SJohannes Altmanninger                            diff::NodeId Dst) {
355e0fe5cd4SJohannes Altmanninger   const diff::Node &DstNode = DstTree.getNode(Dst);
356e0fe5cd4SJohannes Altmanninger   diff::NodeId Src = Diff.getMapped(DstTree, Dst);
357e0fe5cd4SJohannes Altmanninger   switch (DstNode.Change) {
358e0fe5cd4SJohannes Altmanninger   case diff::None:
359e0fe5cd4SJohannes Altmanninger     break;
360e0fe5cd4SJohannes Altmanninger   case diff::Delete:
361e0fe5cd4SJohannes Altmanninger     llvm_unreachable("The destination tree can't have deletions.");
362e0fe5cd4SJohannes Altmanninger   case diff::Update:
363e0fe5cd4SJohannes Altmanninger     OS << "Update ";
364e0fe5cd4SJohannes Altmanninger     printNode(OS, SrcTree, Src);
365e0fe5cd4SJohannes Altmanninger     OS << " to " << DstTree.getNodeValue(Dst) << "\n";
366e0fe5cd4SJohannes Altmanninger     break;
367e0fe5cd4SJohannes Altmanninger   case diff::Insert:
368e0fe5cd4SJohannes Altmanninger   case diff::Move:
369e0fe5cd4SJohannes Altmanninger   case diff::UpdateMove:
370e0fe5cd4SJohannes Altmanninger     if (DstNode.Change == diff::Insert)
371e0fe5cd4SJohannes Altmanninger       OS << "Insert";
372e0fe5cd4SJohannes Altmanninger     else if (DstNode.Change == diff::Move)
373e0fe5cd4SJohannes Altmanninger       OS << "Move";
374e0fe5cd4SJohannes Altmanninger     else if (DstNode.Change == diff::UpdateMove)
375e0fe5cd4SJohannes Altmanninger       OS << "Update and Move";
376e0fe5cd4SJohannes Altmanninger     OS << " ";
377e0fe5cd4SJohannes Altmanninger     printNode(OS, DstTree, Dst);
378e0fe5cd4SJohannes Altmanninger     OS << " into ";
379e0fe5cd4SJohannes Altmanninger     printNode(OS, DstTree, DstNode.Parent);
380e0fe5cd4SJohannes Altmanninger     OS << " at " << DstTree.findPositionInParent(Dst) << "\n";
381e0fe5cd4SJohannes Altmanninger     break;
382e0fe5cd4SJohannes Altmanninger   }
383e0fe5cd4SJohannes Altmanninger }
384e0fe5cd4SJohannes Altmanninger 
385a75b2cacSAlex Lorenz int main(int argc, const char **argv) {
386849f20e4SJohannes Altmanninger   std::string ErrorMessage;
387849f20e4SJohannes Altmanninger   std::unique_ptr<CompilationDatabase> CommonCompilations =
388849f20e4SJohannes Altmanninger       FixedCompilationDatabase::loadFromCommandLine(argc, argv, ErrorMessage);
389849f20e4SJohannes Altmanninger   if (!CommonCompilations && !ErrorMessage.empty())
390849f20e4SJohannes Altmanninger     llvm::errs() << ErrorMessage;
391a75b2cacSAlex Lorenz   cl::HideUnrelatedOptions(ClangDiffCategory);
392a75b2cacSAlex Lorenz   if (!cl::ParseCommandLineOptions(argc, argv)) {
393a75b2cacSAlex Lorenz     cl::PrintOptionValues();
394a75b2cacSAlex Lorenz     return 1;
395a75b2cacSAlex Lorenz   }
396a75b2cacSAlex Lorenz 
397849f20e4SJohannes Altmanninger   addExtraArgs(CommonCompilations);
398849f20e4SJohannes Altmanninger 
399a1d2b5d5SJohannes Altmanninger   if (ASTDump || ASTDumpJson) {
400a75b2cacSAlex Lorenz     if (!DestinationPath.empty()) {
401a75b2cacSAlex Lorenz       llvm::errs() << "Error: Please specify exactly one filename.\n";
402a75b2cacSAlex Lorenz       return 1;
403a75b2cacSAlex Lorenz     }
404849f20e4SJohannes Altmanninger     std::unique_ptr<ASTUnit> AST = getAST(CommonCompilations, SourcePath);
405a75b2cacSAlex Lorenz     if (!AST)
406a75b2cacSAlex Lorenz       return 1;
407a75b2cacSAlex Lorenz     diff::SyntaxTree Tree(AST->getASTContext());
408a1d2b5d5SJohannes Altmanninger     if (ASTDump) {
409a1d2b5d5SJohannes Altmanninger       printTree(llvm::outs(), Tree);
410a1d2b5d5SJohannes Altmanninger       return 0;
411a1d2b5d5SJohannes Altmanninger     }
4120da12c84SJohannes Altmanninger     llvm::outs() << R"({"filename":")";
4130da12c84SJohannes Altmanninger     printJsonString(llvm::outs(), SourcePath);
4140da12c84SJohannes Altmanninger     llvm::outs() << R"(","root":)";
4150da12c84SJohannes Altmanninger     printNodeAsJson(llvm::outs(), Tree, Tree.getRootId());
4160da12c84SJohannes Altmanninger     llvm::outs() << "}\n";
417a75b2cacSAlex Lorenz     return 0;
418a75b2cacSAlex Lorenz   }
419a75b2cacSAlex Lorenz 
420a75b2cacSAlex Lorenz   if (DestinationPath.empty()) {
421a75b2cacSAlex Lorenz     llvm::errs() << "Error: Exactly two paths are required.\n";
422a75b2cacSAlex Lorenz     return 1;
423a75b2cacSAlex Lorenz   }
424a75b2cacSAlex Lorenz 
425849f20e4SJohannes Altmanninger   std::unique_ptr<ASTUnit> Src = getAST(CommonCompilations, SourcePath);
426849f20e4SJohannes Altmanninger   std::unique_ptr<ASTUnit> Dst = getAST(CommonCompilations, DestinationPath);
427a75b2cacSAlex Lorenz   if (!Src || !Dst)
428a75b2cacSAlex Lorenz     return 1;
429a75b2cacSAlex Lorenz 
430a75b2cacSAlex Lorenz   diff::ComparisonOptions Options;
431849f20e4SJohannes Altmanninger   if (MaxSize != -1)
432849f20e4SJohannes Altmanninger     Options.MaxSize = MaxSize;
433*d1969307SJohannes Altmanninger   if (!StopAfter.empty()) {
434*d1969307SJohannes Altmanninger     if (StopAfter == "topdown")
435*d1969307SJohannes Altmanninger       Options.StopAfterTopDown = true;
436*d1969307SJohannes Altmanninger     else if (StopAfter != "bottomup") {
437*d1969307SJohannes Altmanninger       llvm::errs() << "Error: Invalid argument for -stop-after\n";
438*d1969307SJohannes Altmanninger       return 1;
439*d1969307SJohannes Altmanninger     }
440*d1969307SJohannes Altmanninger   }
441a75b2cacSAlex Lorenz   diff::SyntaxTree SrcTree(Src->getASTContext());
442a75b2cacSAlex Lorenz   diff::SyntaxTree DstTree(Dst->getASTContext());
443e0fe5cd4SJohannes Altmanninger   diff::ASTDiff Diff(SrcTree, DstTree, Options);
444e0fe5cd4SJohannes Altmanninger 
445a29d6aecSJohannes Altmanninger   if (HtmlDiff) {
446a29d6aecSJohannes Altmanninger     llvm::outs() << HtmlDiffHeader << "<pre>";
447a29d6aecSJohannes Altmanninger     llvm::outs() << "<div id='L' class='code'>";
448a29d6aecSJohannes Altmanninger     printHtmlForNode(llvm::outs(), Diff, SrcTree, true, SrcTree.getRootId(), 0);
449a29d6aecSJohannes Altmanninger     llvm::outs() << "</div>";
450a29d6aecSJohannes Altmanninger     llvm::outs() << "<div id='R' class='code'>";
451a29d6aecSJohannes Altmanninger     printHtmlForNode(llvm::outs(), Diff, DstTree, false, DstTree.getRootId(),
452a29d6aecSJohannes Altmanninger                      0);
453a29d6aecSJohannes Altmanninger     llvm::outs() << "</div>";
454a29d6aecSJohannes Altmanninger     llvm::outs() << "</pre></div></body></html>\n";
455a29d6aecSJohannes Altmanninger     return 0;
456a29d6aecSJohannes Altmanninger   }
457a29d6aecSJohannes Altmanninger 
458e0fe5cd4SJohannes Altmanninger   for (diff::NodeId Dst : DstTree) {
459e0fe5cd4SJohannes Altmanninger     diff::NodeId Src = Diff.getMapped(DstTree, Dst);
460683876caSJohannes Altmanninger     if (PrintMatches && Src.isValid()) {
461e0fe5cd4SJohannes Altmanninger       llvm::outs() << "Match ";
462e0fe5cd4SJohannes Altmanninger       printNode(llvm::outs(), SrcTree, Src);
463e0fe5cd4SJohannes Altmanninger       llvm::outs() << " to ";
464e0fe5cd4SJohannes Altmanninger       printNode(llvm::outs(), DstTree, Dst);
465e0fe5cd4SJohannes Altmanninger       llvm::outs() << "\n";
466e0fe5cd4SJohannes Altmanninger     }
467e0fe5cd4SJohannes Altmanninger     printDstChange(llvm::outs(), Diff, SrcTree, DstTree, Dst);
468e0fe5cd4SJohannes Altmanninger   }
469e0fe5cd4SJohannes Altmanninger   for (diff::NodeId Src : SrcTree) {
470e0fe5cd4SJohannes Altmanninger     if (Diff.getMapped(SrcTree, Src).isInvalid()) {
471e0fe5cd4SJohannes Altmanninger       llvm::outs() << "Delete ";
472e0fe5cd4SJohannes Altmanninger       printNode(llvm::outs(), SrcTree, Src);
473e0fe5cd4SJohannes Altmanninger       llvm::outs() << "\n";
474e0fe5cd4SJohannes Altmanninger     }
475e0fe5cd4SJohannes Altmanninger   }
476a75b2cacSAlex Lorenz 
477a75b2cacSAlex Lorenz   return 0;
478a75b2cacSAlex Lorenz }
479