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 << "&";
225e5dd7070Spatrick break;
226e5dd7070Spatrick case '<':
227e5dd7070Spatrick OS << "<";
228e5dd7070Spatrick break;
229e5dd7070Spatrick case '>':
230e5dd7070Spatrick OS << ">";
231e5dd7070Spatrick break;
232e5dd7070Spatrick case '\'':
233e5dd7070Spatrick OS << "'";
234e5dd7070Spatrick break;
235e5dd7070Spatrick case '"':
236e5dd7070Spatrick OS << """;
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