ClangDiff.cpp revision 1.1.1.2 1 1.1 joerg //===- ClangDiff.cpp - compare source files by AST nodes ------*- C++ -*- -===//
2 1.1 joerg //
3 1.1 joerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 1.1 joerg // See https://llvm.org/LICENSE.txt for license information.
5 1.1 joerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 1.1 joerg //
7 1.1 joerg //===----------------------------------------------------------------------===//
8 1.1 joerg //
9 1.1 joerg // This file implements a tool for syntax tree based comparison using
10 1.1 joerg // Tooling/ASTDiff.
11 1.1 joerg //
12 1.1 joerg //===----------------------------------------------------------------------===//
13 1.1 joerg
14 1.1 joerg #include "clang/Tooling/ASTDiff/ASTDiff.h"
15 1.1 joerg #include "clang/Tooling/CommonOptionsParser.h"
16 1.1 joerg #include "clang/Tooling/Tooling.h"
17 1.1 joerg #include "llvm/Support/CommandLine.h"
18 1.1 joerg
19 1.1 joerg using namespace llvm;
20 1.1 joerg using namespace clang;
21 1.1 joerg using namespace clang::tooling;
22 1.1 joerg
23 1.1 joerg static cl::OptionCategory ClangDiffCategory("clang-diff options");
24 1.1 joerg
25 1.1 joerg static cl::opt<bool>
26 1.1 joerg ASTDump("ast-dump",
27 1.1 joerg cl::desc("Print the internal representation of the AST."),
28 1.1 joerg cl::init(false), cl::cat(ClangDiffCategory));
29 1.1 joerg
30 1.1 joerg static cl::opt<bool> ASTDumpJson(
31 1.1 joerg "ast-dump-json",
32 1.1 joerg cl::desc("Print the internal representation of the AST as JSON."),
33 1.1 joerg cl::init(false), cl::cat(ClangDiffCategory));
34 1.1 joerg
35 1.1 joerg static cl::opt<bool> PrintMatches("dump-matches",
36 1.1 joerg cl::desc("Print the matched nodes."),
37 1.1 joerg cl::init(false), cl::cat(ClangDiffCategory));
38 1.1 joerg
39 1.1 joerg static cl::opt<bool> HtmlDiff("html",
40 1.1 joerg cl::desc("Output a side-by-side diff in HTML."),
41 1.1 joerg cl::init(false), cl::cat(ClangDiffCategory));
42 1.1 joerg
43 1.1 joerg static cl::opt<std::string> SourcePath(cl::Positional, cl::desc("<source>"),
44 1.1 joerg cl::Required,
45 1.1 joerg cl::cat(ClangDiffCategory));
46 1.1 joerg
47 1.1 joerg static cl::opt<std::string> DestinationPath(cl::Positional,
48 1.1 joerg cl::desc("<destination>"),
49 1.1 joerg cl::Optional,
50 1.1 joerg cl::cat(ClangDiffCategory));
51 1.1 joerg
52 1.1 joerg static cl::opt<std::string> StopAfter("stop-diff-after",
53 1.1 joerg cl::desc("<topdown|bottomup>"),
54 1.1 joerg cl::Optional, cl::init(""),
55 1.1 joerg cl::cat(ClangDiffCategory));
56 1.1 joerg
57 1.1 joerg static cl::opt<int> MaxSize("s", cl::desc("<maxsize>"), cl::Optional,
58 1.1 joerg cl::init(-1), cl::cat(ClangDiffCategory));
59 1.1 joerg
60 1.1 joerg static cl::opt<std::string> BuildPath("p", cl::desc("Build path"), cl::init(""),
61 1.1 joerg cl::Optional, cl::cat(ClangDiffCategory));
62 1.1 joerg
63 1.1 joerg static cl::list<std::string> ArgsAfter(
64 1.1 joerg "extra-arg",
65 1.1 joerg cl::desc("Additional argument to append to the compiler command line"),
66 1.1 joerg cl::cat(ClangDiffCategory));
67 1.1 joerg
68 1.1 joerg static cl::list<std::string> ArgsBefore(
69 1.1 joerg "extra-arg-before",
70 1.1 joerg cl::desc("Additional argument to prepend to the compiler command line"),
71 1.1 joerg cl::cat(ClangDiffCategory));
72 1.1 joerg
73 1.1 joerg static void addExtraArgs(std::unique_ptr<CompilationDatabase> &Compilations) {
74 1.1 joerg if (!Compilations)
75 1.1 joerg return;
76 1.1 joerg auto AdjustingCompilations =
77 1.1 joerg std::make_unique<ArgumentsAdjustingCompilations>(
78 1.1 joerg std::move(Compilations));
79 1.1 joerg AdjustingCompilations->appendArgumentsAdjuster(
80 1.1 joerg getInsertArgumentAdjuster(ArgsBefore, ArgumentInsertPosition::BEGIN));
81 1.1 joerg AdjustingCompilations->appendArgumentsAdjuster(
82 1.1 joerg getInsertArgumentAdjuster(ArgsAfter, ArgumentInsertPosition::END));
83 1.1 joerg Compilations = std::move(AdjustingCompilations);
84 1.1 joerg }
85 1.1 joerg
86 1.1 joerg static std::unique_ptr<ASTUnit>
87 1.1 joerg getAST(const std::unique_ptr<CompilationDatabase> &CommonCompilations,
88 1.1 joerg const StringRef Filename) {
89 1.1 joerg std::string ErrorMessage;
90 1.1 joerg std::unique_ptr<CompilationDatabase> Compilations;
91 1.1 joerg if (!CommonCompilations) {
92 1.1 joerg Compilations = CompilationDatabase::autoDetectFromSource(
93 1.1 joerg BuildPath.empty() ? Filename : BuildPath, ErrorMessage);
94 1.1 joerg if (!Compilations) {
95 1.1 joerg llvm::errs()
96 1.1 joerg << "Error while trying to load a compilation database, running "
97 1.1 joerg "without flags.\n"
98 1.1 joerg << ErrorMessage;
99 1.1 joerg Compilations =
100 1.1 joerg std::make_unique<clang::tooling::FixedCompilationDatabase>(
101 1.1 joerg ".", std::vector<std::string>());
102 1.1 joerg }
103 1.1 joerg }
104 1.1 joerg addExtraArgs(Compilations);
105 1.1.1.2 joerg std::array<std::string, 1> Files = {{std::string(Filename)}};
106 1.1 joerg ClangTool Tool(Compilations ? *Compilations : *CommonCompilations, Files);
107 1.1 joerg std::vector<std::unique_ptr<ASTUnit>> ASTs;
108 1.1 joerg Tool.buildASTs(ASTs);
109 1.1 joerg if (ASTs.size() != Files.size())
110 1.1 joerg return nullptr;
111 1.1 joerg return std::move(ASTs[0]);
112 1.1 joerg }
113 1.1 joerg
114 1.1 joerg static char hexdigit(int N) { return N &= 0xf, N + (N < 10 ? '0' : 'a' - 10); }
115 1.1 joerg
116 1.1 joerg static const char HtmlDiffHeader[] = R"(
117 1.1 joerg <html>
118 1.1 joerg <head>
119 1.1 joerg <meta charset='utf-8'/>
120 1.1 joerg <style>
121 1.1 joerg span.d { color: red; }
122 1.1 joerg span.u { color: #cc00cc; }
123 1.1 joerg span.i { color: green; }
124 1.1 joerg span.m { font-weight: bold; }
125 1.1 joerg span { font-weight: normal; color: black; }
126 1.1 joerg div.code {
127 1.1 joerg width: 48%;
128 1.1 joerg height: 98%;
129 1.1 joerg overflow: scroll;
130 1.1 joerg float: left;
131 1.1 joerg padding: 0 0 0.5% 0.5%;
132 1.1 joerg border: solid 2px LightGrey;
133 1.1 joerg border-radius: 5px;
134 1.1 joerg }
135 1.1 joerg </style>
136 1.1 joerg </head>
137 1.1 joerg <script type='text/javascript'>
138 1.1 joerg highlightStack = []
139 1.1 joerg function clearHighlight() {
140 1.1 joerg while (highlightStack.length) {
141 1.1 joerg var [l, r] = highlightStack.pop()
142 1.1 joerg document.getElementById(l).style.backgroundColor = 'inherit'
143 1.1 joerg if (r[1] != '-')
144 1.1 joerg document.getElementById(r).style.backgroundColor = 'inherit'
145 1.1 joerg }
146 1.1 joerg }
147 1.1 joerg function highlight(event) {
148 1.1 joerg var id = event.target['id']
149 1.1 joerg doHighlight(id)
150 1.1 joerg }
151 1.1 joerg function doHighlight(id) {
152 1.1 joerg clearHighlight()
153 1.1 joerg source = document.getElementById(id)
154 1.1 joerg if (!source.attributes['tid'])
155 1.1 joerg return
156 1.1 joerg var mapped = source
157 1.1 joerg while (mapped && mapped.parentElement && mapped.attributes['tid'].value.substr(1) === '-1')
158 1.1 joerg mapped = mapped.parentElement
159 1.1 joerg var tid = null, target = null
160 1.1 joerg if (mapped) {
161 1.1 joerg tid = mapped.attributes['tid'].value
162 1.1 joerg target = document.getElementById(tid)
163 1.1 joerg }
164 1.1 joerg if (source.parentElement && source.parentElement.classList.contains('code'))
165 1.1 joerg return
166 1.1 joerg source.style.backgroundColor = 'lightgrey'
167 1.1 joerg source.scrollIntoView()
168 1.1 joerg if (target) {
169 1.1 joerg if (mapped === source)
170 1.1 joerg target.style.backgroundColor = 'lightgrey'
171 1.1 joerg target.scrollIntoView()
172 1.1 joerg }
173 1.1 joerg highlightStack.push([id, tid])
174 1.1 joerg location.hash = '#' + id
175 1.1 joerg }
176 1.1 joerg function scrollToBoth() {
177 1.1 joerg doHighlight(location.hash.substr(1))
178 1.1 joerg }
179 1.1 joerg function changed(elem) {
180 1.1 joerg return elem.classList.length == 0
181 1.1 joerg }
182 1.1 joerg function nextChangedNode(prefix, increment, number) {
183 1.1 joerg do {
184 1.1 joerg number += increment
185 1.1 joerg var elem = document.getElementById(prefix + number)
186 1.1 joerg } while(elem && !changed(elem))
187 1.1 joerg return elem ? number : null
188 1.1 joerg }
189 1.1 joerg function handleKey(e) {
190 1.1 joerg var down = e.code === "KeyJ"
191 1.1 joerg var up = e.code === "KeyK"
192 1.1 joerg if (!down && !up)
193 1.1 joerg return
194 1.1 joerg var id = highlightStack[0] ? highlightStack[0][0] : 'R0'
195 1.1 joerg var oldelem = document.getElementById(id)
196 1.1 joerg var number = parseInt(id.substr(1))
197 1.1 joerg var increment = down ? 1 : -1
198 1.1 joerg var lastnumber = number
199 1.1 joerg var prefix = id[0]
200 1.1 joerg do {
201 1.1 joerg number = nextChangedNode(prefix, increment, number)
202 1.1 joerg var elem = document.getElementById(prefix + number)
203 1.1 joerg if (up && elem) {
204 1.1 joerg while (elem.parentElement && changed(elem.parentElement))
205 1.1 joerg elem = elem.parentElement
206 1.1 joerg number = elem.id.substr(1)
207 1.1 joerg }
208 1.1 joerg } while ((down && id !== 'R0' && oldelem.contains(elem)))
209 1.1 joerg if (!number)
210 1.1 joerg number = lastnumber
211 1.1 joerg elem = document.getElementById(prefix + number)
212 1.1 joerg doHighlight(prefix + number)
213 1.1 joerg }
214 1.1 joerg window.onload = scrollToBoth
215 1.1 joerg window.onkeydown = handleKey
216 1.1 joerg </script>
217 1.1 joerg <body>
218 1.1 joerg <div onclick='highlight(event)'>
219 1.1 joerg )";
220 1.1 joerg
221 1.1 joerg static void printHtml(raw_ostream &OS, char C) {
222 1.1 joerg switch (C) {
223 1.1 joerg case '&':
224 1.1 joerg OS << "&";
225 1.1 joerg break;
226 1.1 joerg case '<':
227 1.1 joerg OS << "<";
228 1.1 joerg break;
229 1.1 joerg case '>':
230 1.1 joerg OS << ">";
231 1.1 joerg break;
232 1.1 joerg case '\'':
233 1.1 joerg OS << "'";
234 1.1 joerg break;
235 1.1 joerg case '"':
236 1.1 joerg OS << """;
237 1.1 joerg break;
238 1.1 joerg default:
239 1.1 joerg OS << C;
240 1.1 joerg }
241 1.1 joerg }
242 1.1 joerg
243 1.1 joerg static void printHtml(raw_ostream &OS, const StringRef Str) {
244 1.1 joerg for (char C : Str)
245 1.1 joerg printHtml(OS, C);
246 1.1 joerg }
247 1.1 joerg
248 1.1 joerg static std::string getChangeKindAbbr(diff::ChangeKind Kind) {
249 1.1 joerg switch (Kind) {
250 1.1 joerg case diff::None:
251 1.1 joerg return "";
252 1.1 joerg case diff::Delete:
253 1.1 joerg return "d";
254 1.1 joerg case diff::Update:
255 1.1 joerg return "u";
256 1.1 joerg case diff::Insert:
257 1.1 joerg return "i";
258 1.1 joerg case diff::Move:
259 1.1 joerg return "m";
260 1.1 joerg case diff::UpdateMove:
261 1.1 joerg return "u m";
262 1.1 joerg }
263 1.1 joerg llvm_unreachable("Invalid enumeration value.");
264 1.1 joerg }
265 1.1 joerg
266 1.1 joerg static unsigned printHtmlForNode(raw_ostream &OS, const diff::ASTDiff &Diff,
267 1.1 joerg diff::SyntaxTree &Tree, bool IsLeft,
268 1.1 joerg diff::NodeId Id, unsigned Offset) {
269 1.1 joerg const diff::Node &Node = Tree.getNode(Id);
270 1.1 joerg char MyTag, OtherTag;
271 1.1 joerg diff::NodeId LeftId, RightId;
272 1.1 joerg diff::NodeId TargetId = Diff.getMapped(Tree, Id);
273 1.1 joerg if (IsLeft) {
274 1.1 joerg MyTag = 'L';
275 1.1 joerg OtherTag = 'R';
276 1.1 joerg LeftId = Id;
277 1.1 joerg RightId = TargetId;
278 1.1 joerg } else {
279 1.1 joerg MyTag = 'R';
280 1.1 joerg OtherTag = 'L';
281 1.1 joerg LeftId = TargetId;
282 1.1 joerg RightId = Id;
283 1.1 joerg }
284 1.1 joerg unsigned Begin, End;
285 1.1 joerg std::tie(Begin, End) = Tree.getSourceRangeOffsets(Node);
286 1.1 joerg const SourceManager &SrcMgr = Tree.getASTContext().getSourceManager();
287 1.1.1.2 joerg auto Code = SrcMgr.getBufferOrFake(SrcMgr.getMainFileID()).getBuffer();
288 1.1 joerg for (; Offset < Begin; ++Offset)
289 1.1 joerg printHtml(OS, Code[Offset]);
290 1.1 joerg OS << "<span id='" << MyTag << Id << "' "
291 1.1 joerg << "tid='" << OtherTag << TargetId << "' ";
292 1.1 joerg OS << "title='";
293 1.1 joerg printHtml(OS, Node.getTypeLabel());
294 1.1 joerg OS << "\n" << LeftId << " -> " << RightId;
295 1.1 joerg std::string Value = Tree.getNodeValue(Node);
296 1.1 joerg if (!Value.empty()) {
297 1.1 joerg OS << "\n";
298 1.1 joerg printHtml(OS, Value);
299 1.1 joerg }
300 1.1 joerg OS << "'";
301 1.1 joerg if (Node.Change != diff::None)
302 1.1 joerg OS << " class='" << getChangeKindAbbr(Node.Change) << "'";
303 1.1 joerg OS << ">";
304 1.1 joerg
305 1.1 joerg for (diff::NodeId Child : Node.Children)
306 1.1 joerg Offset = printHtmlForNode(OS, Diff, Tree, IsLeft, Child, Offset);
307 1.1 joerg
308 1.1 joerg for (; Offset < End; ++Offset)
309 1.1 joerg printHtml(OS, Code[Offset]);
310 1.1 joerg if (Id == Tree.getRootId()) {
311 1.1 joerg End = Code.size();
312 1.1 joerg for (; Offset < End; ++Offset)
313 1.1 joerg printHtml(OS, Code[Offset]);
314 1.1 joerg }
315 1.1 joerg OS << "</span>";
316 1.1 joerg return Offset;
317 1.1 joerg }
318 1.1 joerg
319 1.1 joerg static void printJsonString(raw_ostream &OS, const StringRef Str) {
320 1.1 joerg for (signed char C : Str) {
321 1.1 joerg switch (C) {
322 1.1 joerg case '"':
323 1.1 joerg OS << R"(\")";
324 1.1 joerg break;
325 1.1 joerg case '\\':
326 1.1 joerg OS << R"(\\)";
327 1.1 joerg break;
328 1.1 joerg case '\n':
329 1.1 joerg OS << R"(\n)";
330 1.1 joerg break;
331 1.1 joerg case '\t':
332 1.1 joerg OS << R"(\t)";
333 1.1 joerg break;
334 1.1 joerg default:
335 1.1 joerg if ('\x00' <= C && C <= '\x1f') {
336 1.1 joerg OS << R"(\u00)" << hexdigit(C >> 4) << hexdigit(C);
337 1.1 joerg } else {
338 1.1 joerg OS << C;
339 1.1 joerg }
340 1.1 joerg }
341 1.1 joerg }
342 1.1 joerg }
343 1.1 joerg
344 1.1 joerg static void printNodeAttributes(raw_ostream &OS, diff::SyntaxTree &Tree,
345 1.1 joerg diff::NodeId Id) {
346 1.1 joerg const diff::Node &N = Tree.getNode(Id);
347 1.1 joerg OS << R"("id":)" << int(Id);
348 1.1 joerg OS << R"(,"type":")" << N.getTypeLabel() << '"';
349 1.1 joerg auto Offsets = Tree.getSourceRangeOffsets(N);
350 1.1 joerg OS << R"(,"begin":)" << Offsets.first;
351 1.1 joerg OS << R"(,"end":)" << Offsets.second;
352 1.1 joerg std::string Value = Tree.getNodeValue(N);
353 1.1 joerg if (!Value.empty()) {
354 1.1 joerg OS << R"(,"value":")";
355 1.1 joerg printJsonString(OS, Value);
356 1.1 joerg OS << '"';
357 1.1 joerg }
358 1.1 joerg }
359 1.1 joerg
360 1.1 joerg static void printNodeAsJson(raw_ostream &OS, diff::SyntaxTree &Tree,
361 1.1 joerg diff::NodeId Id) {
362 1.1 joerg const diff::Node &N = Tree.getNode(Id);
363 1.1 joerg OS << "{";
364 1.1 joerg printNodeAttributes(OS, Tree, Id);
365 1.1 joerg auto Identifier = N.getIdentifier();
366 1.1 joerg auto QualifiedIdentifier = N.getQualifiedIdentifier();
367 1.1 joerg if (Identifier) {
368 1.1 joerg OS << R"(,"identifier":")";
369 1.1 joerg printJsonString(OS, *Identifier);
370 1.1 joerg OS << R"(")";
371 1.1 joerg if (QualifiedIdentifier && *Identifier != *QualifiedIdentifier) {
372 1.1 joerg OS << R"(,"qualified_identifier":")";
373 1.1 joerg printJsonString(OS, *QualifiedIdentifier);
374 1.1 joerg OS << R"(")";
375 1.1 joerg }
376 1.1 joerg }
377 1.1 joerg OS << R"(,"children":[)";
378 1.1 joerg if (N.Children.size() > 0) {
379 1.1 joerg printNodeAsJson(OS, Tree, N.Children[0]);
380 1.1 joerg for (size_t I = 1, E = N.Children.size(); I < E; ++I) {
381 1.1 joerg OS << ",";
382 1.1 joerg printNodeAsJson(OS, Tree, N.Children[I]);
383 1.1 joerg }
384 1.1 joerg }
385 1.1 joerg OS << "]}";
386 1.1 joerg }
387 1.1 joerg
388 1.1 joerg static void printNode(raw_ostream &OS, diff::SyntaxTree &Tree,
389 1.1 joerg diff::NodeId Id) {
390 1.1 joerg if (Id.isInvalid()) {
391 1.1 joerg OS << "None";
392 1.1 joerg return;
393 1.1 joerg }
394 1.1 joerg OS << Tree.getNode(Id).getTypeLabel();
395 1.1 joerg std::string Value = Tree.getNodeValue(Id);
396 1.1 joerg if (!Value.empty())
397 1.1 joerg OS << ": " << Value;
398 1.1 joerg OS << "(" << Id << ")";
399 1.1 joerg }
400 1.1 joerg
401 1.1 joerg static void printTree(raw_ostream &OS, diff::SyntaxTree &Tree) {
402 1.1 joerg for (diff::NodeId Id : Tree) {
403 1.1 joerg for (int I = 0; I < Tree.getNode(Id).Depth; ++I)
404 1.1 joerg OS << " ";
405 1.1 joerg printNode(OS, Tree, Id);
406 1.1 joerg OS << "\n";
407 1.1 joerg }
408 1.1 joerg }
409 1.1 joerg
410 1.1 joerg static void printDstChange(raw_ostream &OS, diff::ASTDiff &Diff,
411 1.1 joerg diff::SyntaxTree &SrcTree, diff::SyntaxTree &DstTree,
412 1.1 joerg diff::NodeId Dst) {
413 1.1 joerg const diff::Node &DstNode = DstTree.getNode(Dst);
414 1.1 joerg diff::NodeId Src = Diff.getMapped(DstTree, Dst);
415 1.1 joerg switch (DstNode.Change) {
416 1.1 joerg case diff::None:
417 1.1 joerg break;
418 1.1 joerg case diff::Delete:
419 1.1 joerg llvm_unreachable("The destination tree can't have deletions.");
420 1.1 joerg case diff::Update:
421 1.1 joerg OS << "Update ";
422 1.1 joerg printNode(OS, SrcTree, Src);
423 1.1 joerg OS << " to " << DstTree.getNodeValue(Dst) << "\n";
424 1.1 joerg break;
425 1.1 joerg case diff::Insert:
426 1.1 joerg case diff::Move:
427 1.1 joerg case diff::UpdateMove:
428 1.1 joerg if (DstNode.Change == diff::Insert)
429 1.1 joerg OS << "Insert";
430 1.1 joerg else if (DstNode.Change == diff::Move)
431 1.1 joerg OS << "Move";
432 1.1 joerg else if (DstNode.Change == diff::UpdateMove)
433 1.1 joerg OS << "Update and Move";
434 1.1 joerg OS << " ";
435 1.1 joerg printNode(OS, DstTree, Dst);
436 1.1 joerg OS << " into ";
437 1.1 joerg printNode(OS, DstTree, DstNode.Parent);
438 1.1 joerg OS << " at " << DstTree.findPositionInParent(Dst) << "\n";
439 1.1 joerg break;
440 1.1 joerg }
441 1.1 joerg }
442 1.1 joerg
443 1.1 joerg int main(int argc, const char **argv) {
444 1.1 joerg std::string ErrorMessage;
445 1.1 joerg std::unique_ptr<CompilationDatabase> CommonCompilations =
446 1.1 joerg FixedCompilationDatabase::loadFromCommandLine(argc, argv, ErrorMessage);
447 1.1 joerg if (!CommonCompilations && !ErrorMessage.empty())
448 1.1 joerg llvm::errs() << ErrorMessage;
449 1.1 joerg cl::HideUnrelatedOptions(ClangDiffCategory);
450 1.1 joerg if (!cl::ParseCommandLineOptions(argc, argv)) {
451 1.1 joerg cl::PrintOptionValues();
452 1.1 joerg return 1;
453 1.1 joerg }
454 1.1 joerg
455 1.1 joerg addExtraArgs(CommonCompilations);
456 1.1 joerg
457 1.1 joerg if (ASTDump || ASTDumpJson) {
458 1.1 joerg if (!DestinationPath.empty()) {
459 1.1 joerg llvm::errs() << "Error: Please specify exactly one filename.\n";
460 1.1 joerg return 1;
461 1.1 joerg }
462 1.1 joerg std::unique_ptr<ASTUnit> AST = getAST(CommonCompilations, SourcePath);
463 1.1 joerg if (!AST)
464 1.1 joerg return 1;
465 1.1 joerg diff::SyntaxTree Tree(AST->getASTContext());
466 1.1 joerg if (ASTDump) {
467 1.1 joerg printTree(llvm::outs(), Tree);
468 1.1 joerg return 0;
469 1.1 joerg }
470 1.1 joerg llvm::outs() << R"({"filename":")";
471 1.1 joerg printJsonString(llvm::outs(), SourcePath);
472 1.1 joerg llvm::outs() << R"(","root":)";
473 1.1 joerg printNodeAsJson(llvm::outs(), Tree, Tree.getRootId());
474 1.1 joerg llvm::outs() << "}\n";
475 1.1 joerg return 0;
476 1.1 joerg }
477 1.1 joerg
478 1.1 joerg if (DestinationPath.empty()) {
479 1.1 joerg llvm::errs() << "Error: Exactly two paths are required.\n";
480 1.1 joerg return 1;
481 1.1 joerg }
482 1.1 joerg
483 1.1 joerg std::unique_ptr<ASTUnit> Src = getAST(CommonCompilations, SourcePath);
484 1.1 joerg std::unique_ptr<ASTUnit> Dst = getAST(CommonCompilations, DestinationPath);
485 1.1 joerg if (!Src || !Dst)
486 1.1 joerg return 1;
487 1.1 joerg
488 1.1 joerg diff::ComparisonOptions Options;
489 1.1 joerg if (MaxSize != -1)
490 1.1 joerg Options.MaxSize = MaxSize;
491 1.1 joerg if (!StopAfter.empty()) {
492 1.1 joerg if (StopAfter == "topdown")
493 1.1 joerg Options.StopAfterTopDown = true;
494 1.1 joerg else if (StopAfter != "bottomup") {
495 1.1 joerg llvm::errs() << "Error: Invalid argument for -stop-after\n";
496 1.1 joerg return 1;
497 1.1 joerg }
498 1.1 joerg }
499 1.1 joerg diff::SyntaxTree SrcTree(Src->getASTContext());
500 1.1 joerg diff::SyntaxTree DstTree(Dst->getASTContext());
501 1.1 joerg diff::ASTDiff Diff(SrcTree, DstTree, Options);
502 1.1 joerg
503 1.1 joerg if (HtmlDiff) {
504 1.1 joerg llvm::outs() << HtmlDiffHeader << "<pre>";
505 1.1 joerg llvm::outs() << "<div id='L' class='code'>";
506 1.1 joerg printHtmlForNode(llvm::outs(), Diff, SrcTree, true, SrcTree.getRootId(), 0);
507 1.1 joerg llvm::outs() << "</div>";
508 1.1 joerg llvm::outs() << "<div id='R' class='code'>";
509 1.1 joerg printHtmlForNode(llvm::outs(), Diff, DstTree, false, DstTree.getRootId(),
510 1.1 joerg 0);
511 1.1 joerg llvm::outs() << "</div>";
512 1.1 joerg llvm::outs() << "</pre></div></body></html>\n";
513 1.1 joerg return 0;
514 1.1 joerg }
515 1.1 joerg
516 1.1 joerg for (diff::NodeId Dst : DstTree) {
517 1.1 joerg diff::NodeId Src = Diff.getMapped(DstTree, Dst);
518 1.1 joerg if (PrintMatches && Src.isValid()) {
519 1.1 joerg llvm::outs() << "Match ";
520 1.1 joerg printNode(llvm::outs(), SrcTree, Src);
521 1.1 joerg llvm::outs() << " to ";
522 1.1 joerg printNode(llvm::outs(), DstTree, Dst);
523 1.1 joerg llvm::outs() << "\n";
524 1.1 joerg }
525 1.1 joerg printDstChange(llvm::outs(), Diff, SrcTree, DstTree, Dst);
526 1.1 joerg }
527 1.1 joerg for (diff::NodeId Src : SrcTree) {
528 1.1 joerg if (Diff.getMapped(SrcTree, Src).isInvalid()) {
529 1.1 joerg llvm::outs() << "Delete ";
530 1.1 joerg printNode(llvm::outs(), SrcTree, Src);
531 1.1 joerg llvm::outs() << "\n";
532 1.1 joerg }
533 1.1 joerg }
534 1.1 joerg
535 1.1 joerg return 0;
536 1.1 joerg }
537