1a75b2cacSAlex Lorenz //===- ClangDiff.cpp - compare source files by AST nodes ------*- C++ -*- -===//
2a75b2cacSAlex Lorenz //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6a75b2cacSAlex Lorenz //
7a75b2cacSAlex Lorenz //===----------------------------------------------------------------------===//
8a75b2cacSAlex Lorenz //
9a75b2cacSAlex Lorenz // This file implements a tool for syntax tree based comparison using
10a75b2cacSAlex Lorenz // Tooling/ASTDiff.
11a75b2cacSAlex Lorenz //
12a75b2cacSAlex Lorenz //===----------------------------------------------------------------------===//
13a75b2cacSAlex Lorenz
14a75b2cacSAlex Lorenz #include "clang/Tooling/ASTDiff/ASTDiff.h"
15a75b2cacSAlex Lorenz #include "clang/Tooling/CommonOptionsParser.h"
16a75b2cacSAlex Lorenz #include "clang/Tooling/Tooling.h"
17a75b2cacSAlex Lorenz #include "llvm/Support/CommandLine.h"
18a75b2cacSAlex Lorenz
19a75b2cacSAlex Lorenz using namespace llvm;
20a75b2cacSAlex Lorenz using namespace clang;
21a75b2cacSAlex Lorenz using namespace clang::tooling;
22a75b2cacSAlex Lorenz
23a75b2cacSAlex Lorenz static cl::OptionCategory ClangDiffCategory("clang-diff options");
24a75b2cacSAlex Lorenz
25a75b2cacSAlex Lorenz static cl::opt<bool>
26914a958eSJohannes Altmanninger ASTDump("ast-dump",
27a1d2b5d5SJohannes Altmanninger cl::desc("Print the internal representation of the AST."),
28a1d2b5d5SJohannes Altmanninger cl::init(false), cl::cat(ClangDiffCategory));
29a1d2b5d5SJohannes Altmanninger
30a1d2b5d5SJohannes Altmanninger static cl::opt<bool> ASTDumpJson(
31a1d2b5d5SJohannes Altmanninger "ast-dump-json",
32a75b2cacSAlex Lorenz cl::desc("Print the internal representation of the AST as JSON."),
33a75b2cacSAlex Lorenz cl::init(false), cl::cat(ClangDiffCategory));
34a75b2cacSAlex Lorenz
35a584beb9SJohannes Altmanninger static cl::opt<bool> PrintMatches("dump-matches",
36a584beb9SJohannes Altmanninger cl::desc("Print the matched nodes."),
37683876caSJohannes Altmanninger cl::init(false), cl::cat(ClangDiffCategory));
38683876caSJohannes Altmanninger
39a29d6aecSJohannes Altmanninger static cl::opt<bool> HtmlDiff("html",
40a29d6aecSJohannes Altmanninger cl::desc("Output a side-by-side diff in HTML."),
41a29d6aecSJohannes Altmanninger cl::init(false), cl::cat(ClangDiffCategory));
42a29d6aecSJohannes Altmanninger
43a75b2cacSAlex Lorenz static cl::opt<std::string> SourcePath(cl::Positional, cl::desc("<source>"),
44a75b2cacSAlex Lorenz cl::Required,
45a75b2cacSAlex Lorenz cl::cat(ClangDiffCategory));
46a75b2cacSAlex Lorenz
47a75b2cacSAlex Lorenz static cl::opt<std::string> DestinationPath(cl::Positional,
48a75b2cacSAlex Lorenz cl::desc("<destination>"),
49a75b2cacSAlex Lorenz cl::Optional,
50a75b2cacSAlex Lorenz cl::cat(ClangDiffCategory));
51a75b2cacSAlex Lorenz
5246307d73SJacob Gravelle static cl::opt<std::string> StopAfter("stop-diff-after",
53d1969307SJohannes Altmanninger cl::desc("<topdown|bottomup>"),
54d1969307SJohannes Altmanninger cl::Optional, cl::init(""),
55d1969307SJohannes Altmanninger cl::cat(ClangDiffCategory));
56d1969307SJohannes Altmanninger
57849f20e4SJohannes Altmanninger static cl::opt<int> MaxSize("s", cl::desc("<maxsize>"), cl::Optional,
58849f20e4SJohannes Altmanninger cl::init(-1), cl::cat(ClangDiffCategory));
59849f20e4SJohannes Altmanninger
60849f20e4SJohannes Altmanninger static cl::opt<std::string> BuildPath("p", cl::desc("Build path"), cl::init(""),
61849f20e4SJohannes Altmanninger cl::Optional, cl::cat(ClangDiffCategory));
62849f20e4SJohannes Altmanninger
63849f20e4SJohannes Altmanninger static cl::list<std::string> ArgsAfter(
64849f20e4SJohannes Altmanninger "extra-arg",
65849f20e4SJohannes Altmanninger cl::desc("Additional argument to append to the compiler command line"),
66849f20e4SJohannes Altmanninger cl::cat(ClangDiffCategory));
67849f20e4SJohannes Altmanninger
68849f20e4SJohannes Altmanninger static cl::list<std::string> ArgsBefore(
69849f20e4SJohannes Altmanninger "extra-arg-before",
70849f20e4SJohannes Altmanninger cl::desc("Additional argument to prepend to the compiler command line"),
71849f20e4SJohannes Altmanninger cl::cat(ClangDiffCategory));
72849f20e4SJohannes Altmanninger
addExtraArgs(std::unique_ptr<CompilationDatabase> & Compilations)73849f20e4SJohannes Altmanninger static void addExtraArgs(std::unique_ptr<CompilationDatabase> &Compilations) {
74849f20e4SJohannes Altmanninger if (!Compilations)
75849f20e4SJohannes Altmanninger return;
76849f20e4SJohannes Altmanninger auto AdjustingCompilations =
772b3d49b6SJonas Devlieghere std::make_unique<ArgumentsAdjustingCompilations>(
78849f20e4SJohannes Altmanninger std::move(Compilations));
79849f20e4SJohannes Altmanninger AdjustingCompilations->appendArgumentsAdjuster(
80849f20e4SJohannes Altmanninger getInsertArgumentAdjuster(ArgsBefore, ArgumentInsertPosition::BEGIN));
81849f20e4SJohannes Altmanninger AdjustingCompilations->appendArgumentsAdjuster(
82849f20e4SJohannes Altmanninger getInsertArgumentAdjuster(ArgsAfter, ArgumentInsertPosition::END));
83849f20e4SJohannes Altmanninger Compilations = std::move(AdjustingCompilations);
84849f20e4SJohannes Altmanninger }
85849f20e4SJohannes Altmanninger
86849f20e4SJohannes Altmanninger static std::unique_ptr<ASTUnit>
getAST(const std::unique_ptr<CompilationDatabase> & CommonCompilations,const StringRef Filename)87849f20e4SJohannes Altmanninger getAST(const std::unique_ptr<CompilationDatabase> &CommonCompilations,
88849f20e4SJohannes Altmanninger const StringRef Filename) {
89a75b2cacSAlex Lorenz std::string ErrorMessage;
90a75b2cacSAlex Lorenz std::unique_ptr<CompilationDatabase> Compilations;
91849f20e4SJohannes Altmanninger if (!CommonCompilations) {
92849f20e4SJohannes Altmanninger Compilations = CompilationDatabase::autoDetectFromSource(
93849f20e4SJohannes Altmanninger BuildPath.empty() ? Filename : BuildPath, ErrorMessage);
94a75b2cacSAlex Lorenz if (!Compilations) {
95a75b2cacSAlex Lorenz llvm::errs()
96a75b2cacSAlex Lorenz << "Error while trying to load a compilation database, running "
97a75b2cacSAlex Lorenz "without flags.\n"
98a75b2cacSAlex Lorenz << ErrorMessage;
99849f20e4SJohannes Altmanninger Compilations =
1002b3d49b6SJonas Devlieghere std::make_unique<clang::tooling::FixedCompilationDatabase>(
101a75b2cacSAlex Lorenz ".", std::vector<std::string>());
102a75b2cacSAlex Lorenz }
103849f20e4SJohannes Altmanninger }
104849f20e4SJohannes Altmanninger addExtraArgs(Compilations);
105adcd0268SBenjamin Kramer std::array<std::string, 1> Files = {{std::string(Filename)}};
106849f20e4SJohannes Altmanninger ClangTool Tool(Compilations ? *Compilations : *CommonCompilations, Files);
107a75b2cacSAlex Lorenz std::vector<std::unique_ptr<ASTUnit>> ASTs;
108a75b2cacSAlex Lorenz Tool.buildASTs(ASTs);
109a75b2cacSAlex Lorenz if (ASTs.size() != Files.size())
110a75b2cacSAlex Lorenz return nullptr;
111a75b2cacSAlex Lorenz return std::move(ASTs[0]);
112a75b2cacSAlex Lorenz }
113a75b2cacSAlex Lorenz
hexdigit(int N)1140da12c84SJohannes Altmanninger static char hexdigit(int N) { return N &= 0xf, N + (N < 10 ? '0' : 'a' - 10); }
1150da12c84SJohannes Altmanninger
116a29d6aecSJohannes Altmanninger static const char HtmlDiffHeader[] = R"(
117a29d6aecSJohannes Altmanninger <html>
118a29d6aecSJohannes Altmanninger <head>
119a29d6aecSJohannes Altmanninger <meta charset='utf-8'/>
120a29d6aecSJohannes Altmanninger <style>
121a29d6aecSJohannes Altmanninger span.d { color: red; }
122a29d6aecSJohannes Altmanninger span.u { color: #cc00cc; }
123a29d6aecSJohannes Altmanninger span.i { color: green; }
124a29d6aecSJohannes Altmanninger span.m { font-weight: bold; }
125a29d6aecSJohannes Altmanninger span { font-weight: normal; color: black; }
126a29d6aecSJohannes Altmanninger div.code {
127a29d6aecSJohannes Altmanninger width: 48%;
128a29d6aecSJohannes Altmanninger height: 98%;
129a29d6aecSJohannes Altmanninger overflow: scroll;
130a29d6aecSJohannes Altmanninger float: left;
131a29d6aecSJohannes Altmanninger padding: 0 0 0.5% 0.5%;
132a29d6aecSJohannes Altmanninger border: solid 2px LightGrey;
133a29d6aecSJohannes Altmanninger border-radius: 5px;
134a29d6aecSJohannes Altmanninger }
135a29d6aecSJohannes Altmanninger </style>
136a29d6aecSJohannes Altmanninger </head>
137a29d6aecSJohannes Altmanninger <script type='text/javascript'>
138a29d6aecSJohannes Altmanninger highlightStack = []
139a29d6aecSJohannes Altmanninger function clearHighlight() {
140a29d6aecSJohannes Altmanninger while (highlightStack.length) {
1418796049fSJohannes Altmanninger var [l, r] = highlightStack.pop()
14238df0fa6SJohannes Altmanninger document.getElementById(l).style.backgroundColor = 'inherit'
1438796049fSJohannes Altmanninger if (r[1] != '-')
14438df0fa6SJohannes Altmanninger document.getElementById(r).style.backgroundColor = 'inherit'
145a29d6aecSJohannes Altmanninger }
146a29d6aecSJohannes Altmanninger }
147a29d6aecSJohannes Altmanninger function highlight(event) {
1488796049fSJohannes Altmanninger var 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
1568796049fSJohannes Altmanninger var mapped = source
1578796049fSJohannes Altmanninger while (mapped && mapped.parentElement && mapped.attributes['tid'].value.substr(1) === '-1')
1588796049fSJohannes Altmanninger mapped = mapped.parentElement
1598796049fSJohannes Altmanninger var tid = null, target = null
1608796049fSJohannes Altmanninger if (mapped) {
1618796049fSJohannes Altmanninger tid = mapped.attributes['tid'].value
162a29d6aecSJohannes Altmanninger target = document.getElementById(tid)
1638796049fSJohannes Altmanninger }
1648796049fSJohannes Altmanninger if (source.parentElement && source.parentElement.classList.contains('code'))
165a29d6aecSJohannes Altmanninger return
1668796049fSJohannes Altmanninger source.style.backgroundColor = 'lightgrey'
167a29d6aecSJohannes Altmanninger source.scrollIntoView()
1688796049fSJohannes Altmanninger if (target) {
1698796049fSJohannes Altmanninger if (mapped === source)
1708796049fSJohannes Altmanninger target.style.backgroundColor = 'lightgrey'
171a29d6aecSJohannes Altmanninger target.scrollIntoView()
1728796049fSJohannes Altmanninger }
1738796049fSJohannes Altmanninger highlightStack.push([id, tid])
174a29d6aecSJohannes Altmanninger location.hash = '#' + id
175a29d6aecSJohannes Altmanninger }
176a29d6aecSJohannes Altmanninger function scrollToBoth() {
177a29d6aecSJohannes Altmanninger doHighlight(location.hash.substr(1))
178a29d6aecSJohannes Altmanninger }
1798796049fSJohannes Altmanninger function changed(elem) {
1808796049fSJohannes Altmanninger return elem.classList.length == 0
1818796049fSJohannes Altmanninger }
1828796049fSJohannes Altmanninger function nextChangedNode(prefix, increment, number) {
1838796049fSJohannes Altmanninger do {
1848796049fSJohannes Altmanninger number += increment
1858796049fSJohannes Altmanninger var elem = document.getElementById(prefix + number)
1868796049fSJohannes Altmanninger } while(elem && !changed(elem))
1878796049fSJohannes Altmanninger return elem ? number : null
1888796049fSJohannes Altmanninger }
1898796049fSJohannes Altmanninger function handleKey(e) {
1908796049fSJohannes Altmanninger var down = e.code === "KeyJ"
1918796049fSJohannes Altmanninger var up = e.code === "KeyK"
1928796049fSJohannes Altmanninger if (!down && !up)
1938796049fSJohannes Altmanninger return
1948796049fSJohannes Altmanninger var id = highlightStack[0] ? highlightStack[0][0] : 'R0'
1958796049fSJohannes Altmanninger var oldelem = document.getElementById(id)
1968796049fSJohannes Altmanninger var number = parseInt(id.substr(1))
1978796049fSJohannes Altmanninger var increment = down ? 1 : -1
1988796049fSJohannes Altmanninger var lastnumber = number
1998796049fSJohannes Altmanninger var prefix = id[0]
2008796049fSJohannes Altmanninger do {
2018796049fSJohannes Altmanninger number = nextChangedNode(prefix, increment, number)
2028796049fSJohannes Altmanninger var elem = document.getElementById(prefix + number)
2038796049fSJohannes Altmanninger if (up && elem) {
2048796049fSJohannes Altmanninger while (elem.parentElement && changed(elem.parentElement))
2058796049fSJohannes Altmanninger elem = elem.parentElement
2068796049fSJohannes Altmanninger number = elem.id.substr(1)
2078796049fSJohannes Altmanninger }
2088796049fSJohannes Altmanninger } while ((down && id !== 'R0' && oldelem.contains(elem)))
2098796049fSJohannes Altmanninger if (!number)
2108796049fSJohannes Altmanninger number = lastnumber
2118796049fSJohannes Altmanninger elem = document.getElementById(prefix + number)
2128796049fSJohannes Altmanninger doHighlight(prefix + number)
2138796049fSJohannes Altmanninger }
214a29d6aecSJohannes Altmanninger window.onload = scrollToBoth
2158796049fSJohannes Altmanninger window.onkeydown = handleKey
216a29d6aecSJohannes Altmanninger </script>
217a29d6aecSJohannes Altmanninger <body>
218a29d6aecSJohannes Altmanninger <div onclick='highlight(event)'>
219a29d6aecSJohannes Altmanninger )";
220a29d6aecSJohannes Altmanninger
printHtml(raw_ostream & OS,char C)221a29d6aecSJohannes Altmanninger static void printHtml(raw_ostream &OS, char C) {
222a29d6aecSJohannes Altmanninger switch (C) {
223a29d6aecSJohannes Altmanninger case '&':
224a29d6aecSJohannes Altmanninger OS << "&";
225a29d6aecSJohannes Altmanninger break;
226a29d6aecSJohannes Altmanninger case '<':
227a29d6aecSJohannes Altmanninger OS << "<";
228a29d6aecSJohannes Altmanninger break;
229a29d6aecSJohannes Altmanninger case '>':
230a29d6aecSJohannes Altmanninger OS << ">";
231a29d6aecSJohannes Altmanninger break;
232a29d6aecSJohannes Altmanninger case '\'':
233a29d6aecSJohannes Altmanninger OS << "'";
234a29d6aecSJohannes Altmanninger break;
235a29d6aecSJohannes Altmanninger case '"':
236a29d6aecSJohannes Altmanninger OS << """;
237a29d6aecSJohannes Altmanninger break;
238a29d6aecSJohannes Altmanninger default:
239a29d6aecSJohannes Altmanninger OS << C;
240a29d6aecSJohannes Altmanninger }
241a29d6aecSJohannes Altmanninger }
242a29d6aecSJohannes Altmanninger
printHtml(raw_ostream & OS,const StringRef Str)243a29d6aecSJohannes Altmanninger static void printHtml(raw_ostream &OS, const StringRef Str) {
244a29d6aecSJohannes Altmanninger for (char C : Str)
245a29d6aecSJohannes Altmanninger printHtml(OS, C);
246a29d6aecSJohannes Altmanninger }
247a29d6aecSJohannes Altmanninger
getChangeKindAbbr(diff::ChangeKind Kind)248a29d6aecSJohannes Altmanninger static std::string getChangeKindAbbr(diff::ChangeKind Kind) {
249a29d6aecSJohannes Altmanninger switch (Kind) {
250a29d6aecSJohannes Altmanninger case diff::None:
251a29d6aecSJohannes Altmanninger return "";
252a29d6aecSJohannes Altmanninger case diff::Delete:
253a29d6aecSJohannes Altmanninger return "d";
254a29d6aecSJohannes Altmanninger case diff::Update:
255a29d6aecSJohannes Altmanninger return "u";
256a29d6aecSJohannes Altmanninger case diff::Insert:
257a29d6aecSJohannes Altmanninger return "i";
258a29d6aecSJohannes Altmanninger case diff::Move:
259a29d6aecSJohannes Altmanninger return "m";
260a29d6aecSJohannes Altmanninger case diff::UpdateMove:
261a29d6aecSJohannes Altmanninger return "u m";
262a29d6aecSJohannes Altmanninger }
263e1a89fbfSJohannes Altmanninger llvm_unreachable("Invalid enumeration value.");
264a29d6aecSJohannes Altmanninger }
265a29d6aecSJohannes Altmanninger
printHtmlForNode(raw_ostream & OS,const diff::ASTDiff & Diff,diff::SyntaxTree & Tree,bool IsLeft,diff::NodeId Id,unsigned Offset)266a29d6aecSJohannes Altmanninger static unsigned printHtmlForNode(raw_ostream &OS, const diff::ASTDiff &Diff,
267a29d6aecSJohannes Altmanninger diff::SyntaxTree &Tree, bool IsLeft,
268a29d6aecSJohannes Altmanninger diff::NodeId Id, unsigned Offset) {
269a29d6aecSJohannes Altmanninger const diff::Node &Node = Tree.getNode(Id);
270a29d6aecSJohannes Altmanninger char MyTag, OtherTag;
271a29d6aecSJohannes Altmanninger diff::NodeId LeftId, RightId;
272a29d6aecSJohannes Altmanninger diff::NodeId TargetId = Diff.getMapped(Tree, Id);
273a29d6aecSJohannes Altmanninger if (IsLeft) {
274a29d6aecSJohannes Altmanninger MyTag = 'L';
275a29d6aecSJohannes Altmanninger OtherTag = 'R';
276a29d6aecSJohannes Altmanninger LeftId = Id;
277a29d6aecSJohannes Altmanninger RightId = TargetId;
278a29d6aecSJohannes Altmanninger } else {
279a29d6aecSJohannes Altmanninger MyTag = 'R';
280a29d6aecSJohannes Altmanninger OtherTag = 'L';
281a29d6aecSJohannes Altmanninger LeftId = TargetId;
282a29d6aecSJohannes Altmanninger RightId = Id;
283a29d6aecSJohannes Altmanninger }
284a29d6aecSJohannes Altmanninger unsigned Begin, End;
285a29d6aecSJohannes Altmanninger std::tie(Begin, End) = Tree.getSourceRangeOffsets(Node);
286a29d6aecSJohannes Altmanninger const SourceManager &SrcMgr = Tree.getASTContext().getSourceManager();
287*00651981SDuncan P. N. Exon Smith auto Code = SrcMgr.getBufferOrFake(SrcMgr.getMainFileID()).getBuffer();
288a29d6aecSJohannes Altmanninger for (; Offset < Begin; ++Offset)
289a29d6aecSJohannes Altmanninger printHtml(OS, Code[Offset]);
290a29d6aecSJohannes Altmanninger OS << "<span id='" << MyTag << Id << "' "
291a29d6aecSJohannes Altmanninger << "tid='" << OtherTag << TargetId << "' ";
292a29d6aecSJohannes Altmanninger OS << "title='";
293a29d6aecSJohannes Altmanninger printHtml(OS, Node.getTypeLabel());
294a29d6aecSJohannes Altmanninger OS << "\n" << LeftId << " -> " << RightId;
295a29d6aecSJohannes Altmanninger std::string Value = Tree.getNodeValue(Node);
296a29d6aecSJohannes Altmanninger if (!Value.empty()) {
297a29d6aecSJohannes Altmanninger OS << "\n";
298a29d6aecSJohannes Altmanninger printHtml(OS, Value);
299a29d6aecSJohannes Altmanninger }
300a29d6aecSJohannes Altmanninger OS << "'";
301a29d6aecSJohannes Altmanninger if (Node.Change != diff::None)
302a29d6aecSJohannes Altmanninger OS << " class='" << getChangeKindAbbr(Node.Change) << "'";
303a29d6aecSJohannes Altmanninger OS << ">";
304a29d6aecSJohannes Altmanninger
305a29d6aecSJohannes Altmanninger for (diff::NodeId Child : Node.Children)
306a29d6aecSJohannes Altmanninger Offset = printHtmlForNode(OS, Diff, Tree, IsLeft, Child, Offset);
307a29d6aecSJohannes Altmanninger
308a29d6aecSJohannes Altmanninger for (; Offset < End; ++Offset)
309a29d6aecSJohannes Altmanninger printHtml(OS, Code[Offset]);
310a29d6aecSJohannes Altmanninger if (Id == Tree.getRootId()) {
311a29d6aecSJohannes Altmanninger End = Code.size();
312a29d6aecSJohannes Altmanninger for (; Offset < End; ++Offset)
313a29d6aecSJohannes Altmanninger printHtml(OS, Code[Offset]);
314a29d6aecSJohannes Altmanninger }
315a29d6aecSJohannes Altmanninger OS << "</span>";
316a29d6aecSJohannes Altmanninger return Offset;
317a29d6aecSJohannes Altmanninger }
318a29d6aecSJohannes Altmanninger
printJsonString(raw_ostream & OS,const StringRef Str)3190da12c84SJohannes Altmanninger static void printJsonString(raw_ostream &OS, const StringRef Str) {
32014be1839SJohannes Altmanninger for (signed char C : Str) {
3210da12c84SJohannes Altmanninger switch (C) {
3220da12c84SJohannes Altmanninger case '"':
3230da12c84SJohannes Altmanninger OS << R"(\")";
3240da12c84SJohannes Altmanninger break;
3250da12c84SJohannes Altmanninger case '\\':
3260da12c84SJohannes Altmanninger OS << R"(\\)";
3270da12c84SJohannes Altmanninger break;
3280da12c84SJohannes Altmanninger case '\n':
3290da12c84SJohannes Altmanninger OS << R"(\n)";
3300da12c84SJohannes Altmanninger break;
3310da12c84SJohannes Altmanninger case '\t':
3320da12c84SJohannes Altmanninger OS << R"(\t)";
3330da12c84SJohannes Altmanninger break;
3340da12c84SJohannes Altmanninger default:
3350da12c84SJohannes Altmanninger if ('\x00' <= C && C <= '\x1f') {
3360da12c84SJohannes Altmanninger OS << R"(\u00)" << hexdigit(C >> 4) << hexdigit(C);
3370da12c84SJohannes Altmanninger } else {
3380da12c84SJohannes Altmanninger OS << C;
3390da12c84SJohannes Altmanninger }
3400da12c84SJohannes Altmanninger }
3410da12c84SJohannes Altmanninger }
3420da12c84SJohannes Altmanninger }
3430da12c84SJohannes Altmanninger
printNodeAttributes(raw_ostream & OS,diff::SyntaxTree & Tree,diff::NodeId Id)3440da12c84SJohannes Altmanninger static void printNodeAttributes(raw_ostream &OS, diff::SyntaxTree &Tree,
3450da12c84SJohannes Altmanninger diff::NodeId Id) {
3460da12c84SJohannes Altmanninger const diff::Node &N = Tree.getNode(Id);
3470da12c84SJohannes Altmanninger OS << R"("id":)" << int(Id);
3480da12c84SJohannes Altmanninger OS << R"(,"type":")" << N.getTypeLabel() << '"';
3490da12c84SJohannes Altmanninger auto Offsets = Tree.getSourceRangeOffsets(N);
3500da12c84SJohannes Altmanninger OS << R"(,"begin":)" << Offsets.first;
3510da12c84SJohannes Altmanninger OS << R"(,"end":)" << Offsets.second;
352e0fe5cd4SJohannes Altmanninger std::string Value = Tree.getNodeValue(N);
3530da12c84SJohannes Altmanninger if (!Value.empty()) {
3540da12c84SJohannes Altmanninger OS << R"(,"value":")";
3550da12c84SJohannes Altmanninger printJsonString(OS, Value);
3560da12c84SJohannes Altmanninger OS << '"';
3570da12c84SJohannes Altmanninger }
3580da12c84SJohannes Altmanninger }
3590da12c84SJohannes Altmanninger
3600da12c84SJohannes Altmanninger static void printNodeAsJson(raw_ostream &OS, diff::SyntaxTree &Tree,
3610da12c84SJohannes Altmanninger diff::NodeId Id) {
3620da12c84SJohannes Altmanninger const diff::Node &N = Tree.getNode(Id);
3630da12c84SJohannes Altmanninger OS << "{";
3640da12c84SJohannes Altmanninger printNodeAttributes(OS, Tree, Id);
3650dd86dc5SJohannes Altmanninger auto Identifier = N.getIdentifier();
3660dd86dc5SJohannes Altmanninger auto QualifiedIdentifier = N.getQualifiedIdentifier();
3670dd86dc5SJohannes Altmanninger if (Identifier) {
3680dd86dc5SJohannes Altmanninger OS << R"(,"identifier":")";
3690dd86dc5SJohannes Altmanninger printJsonString(OS, *Identifier);
3700dd86dc5SJohannes Altmanninger OS << R"(")";
3710dd86dc5SJohannes Altmanninger if (QualifiedIdentifier && *Identifier != *QualifiedIdentifier) {
3720dd86dc5SJohannes Altmanninger OS << R"(,"qualified_identifier":")";
3730dd86dc5SJohannes Altmanninger printJsonString(OS, *QualifiedIdentifier);
3740dd86dc5SJohannes Altmanninger OS << R"(")";
3750dd86dc5SJohannes Altmanninger }
3760dd86dc5SJohannes Altmanninger }
3770da12c84SJohannes Altmanninger OS << R"(,"children":[)";
3780da12c84SJohannes Altmanninger if (N.Children.size() > 0) {
3790da12c84SJohannes Altmanninger printNodeAsJson(OS, Tree, N.Children[0]);
3800da12c84SJohannes Altmanninger for (size_t I = 1, E = N.Children.size(); I < E; ++I) {
3810da12c84SJohannes Altmanninger OS << ",";
3820da12c84SJohannes Altmanninger printNodeAsJson(OS, Tree, N.Children[I]);
3830da12c84SJohannes Altmanninger }
3840da12c84SJohannes Altmanninger }
3850da12c84SJohannes Altmanninger OS << "]}";
3860da12c84SJohannes Altmanninger }
3870da12c84SJohannes Altmanninger
388e0fe5cd4SJohannes Altmanninger static void printNode(raw_ostream &OS, diff::SyntaxTree &Tree,
389e0fe5cd4SJohannes Altmanninger diff::NodeId Id) {
390e0fe5cd4SJohannes Altmanninger if (Id.isInvalid()) {
391e0fe5cd4SJohannes Altmanninger OS << "None";
392e0fe5cd4SJohannes Altmanninger return;
393e0fe5cd4SJohannes Altmanninger }
394e0fe5cd4SJohannes Altmanninger OS << Tree.getNode(Id).getTypeLabel();
395e0fe5cd4SJohannes Altmanninger std::string Value = Tree.getNodeValue(Id);
396e0fe5cd4SJohannes Altmanninger if (!Value.empty())
397e0fe5cd4SJohannes Altmanninger OS << ": " << Value;
398e0fe5cd4SJohannes Altmanninger OS << "(" << Id << ")";
399e0fe5cd4SJohannes Altmanninger }
400e0fe5cd4SJohannes Altmanninger
401a1d2b5d5SJohannes Altmanninger static void printTree(raw_ostream &OS, diff::SyntaxTree &Tree) {
402a1d2b5d5SJohannes Altmanninger for (diff::NodeId Id : Tree) {
403a1d2b5d5SJohannes Altmanninger for (int I = 0; I < Tree.getNode(Id).Depth; ++I)
404a1d2b5d5SJohannes Altmanninger OS << " ";
405a1d2b5d5SJohannes Altmanninger printNode(OS, Tree, Id);
406a1d2b5d5SJohannes Altmanninger OS << "\n";
407a1d2b5d5SJohannes Altmanninger }
408a1d2b5d5SJohannes Altmanninger }
409a1d2b5d5SJohannes Altmanninger
410e0fe5cd4SJohannes Altmanninger static void printDstChange(raw_ostream &OS, diff::ASTDiff &Diff,
411e0fe5cd4SJohannes Altmanninger diff::SyntaxTree &SrcTree, diff::SyntaxTree &DstTree,
412e0fe5cd4SJohannes Altmanninger diff::NodeId Dst) {
413e0fe5cd4SJohannes Altmanninger const diff::Node &DstNode = DstTree.getNode(Dst);
414e0fe5cd4SJohannes Altmanninger diff::NodeId Src = Diff.getMapped(DstTree, Dst);
415e0fe5cd4SJohannes Altmanninger switch (DstNode.Change) {
416e0fe5cd4SJohannes Altmanninger case diff::None:
417e0fe5cd4SJohannes Altmanninger break;
418e0fe5cd4SJohannes Altmanninger case diff::Delete:
419e0fe5cd4SJohannes Altmanninger llvm_unreachable("The destination tree can't have deletions.");
420e0fe5cd4SJohannes Altmanninger case diff::Update:
421e0fe5cd4SJohannes Altmanninger OS << "Update ";
422e0fe5cd4SJohannes Altmanninger printNode(OS, SrcTree, Src);
423e0fe5cd4SJohannes Altmanninger OS << " to " << DstTree.getNodeValue(Dst) << "\n";
424e0fe5cd4SJohannes Altmanninger break;
425e0fe5cd4SJohannes Altmanninger case diff::Insert:
426e0fe5cd4SJohannes Altmanninger case diff::Move:
427e0fe5cd4SJohannes Altmanninger case diff::UpdateMove:
428e0fe5cd4SJohannes Altmanninger if (DstNode.Change == diff::Insert)
429e0fe5cd4SJohannes Altmanninger OS << "Insert";
430e0fe5cd4SJohannes Altmanninger else if (DstNode.Change == diff::Move)
431e0fe5cd4SJohannes Altmanninger OS << "Move";
432e0fe5cd4SJohannes Altmanninger else if (DstNode.Change == diff::UpdateMove)
433e0fe5cd4SJohannes Altmanninger OS << "Update and Move";
434e0fe5cd4SJohannes Altmanninger OS << " ";
435e0fe5cd4SJohannes Altmanninger printNode(OS, DstTree, Dst);
436e0fe5cd4SJohannes Altmanninger OS << " into ";
437e0fe5cd4SJohannes Altmanninger printNode(OS, DstTree, DstNode.Parent);
438e0fe5cd4SJohannes Altmanninger OS << " at " << DstTree.findPositionInParent(Dst) << "\n";
439e0fe5cd4SJohannes Altmanninger break;
440e0fe5cd4SJohannes Altmanninger }
441e0fe5cd4SJohannes Altmanninger }
442e0fe5cd4SJohannes Altmanninger
443a75b2cacSAlex Lorenz int main(int argc, const char **argv) {
444849f20e4SJohannes Altmanninger std::string ErrorMessage;
445849f20e4SJohannes Altmanninger std::unique_ptr<CompilationDatabase> CommonCompilations =
446849f20e4SJohannes Altmanninger FixedCompilationDatabase::loadFromCommandLine(argc, argv, ErrorMessage);
447849f20e4SJohannes Altmanninger if (!CommonCompilations && !ErrorMessage.empty())
448849f20e4SJohannes Altmanninger llvm::errs() << ErrorMessage;
449a75b2cacSAlex Lorenz cl::HideUnrelatedOptions(ClangDiffCategory);
450a75b2cacSAlex Lorenz if (!cl::ParseCommandLineOptions(argc, argv)) {
451a75b2cacSAlex Lorenz cl::PrintOptionValues();
452a75b2cacSAlex Lorenz return 1;
453a75b2cacSAlex Lorenz }
454a75b2cacSAlex Lorenz
455849f20e4SJohannes Altmanninger addExtraArgs(CommonCompilations);
456849f20e4SJohannes Altmanninger
457a1d2b5d5SJohannes Altmanninger if (ASTDump || ASTDumpJson) {
458a75b2cacSAlex Lorenz if (!DestinationPath.empty()) {
459a75b2cacSAlex Lorenz llvm::errs() << "Error: Please specify exactly one filename.\n";
460a75b2cacSAlex Lorenz return 1;
461a75b2cacSAlex Lorenz }
462849f20e4SJohannes Altmanninger std::unique_ptr<ASTUnit> AST = getAST(CommonCompilations, SourcePath);
463a75b2cacSAlex Lorenz if (!AST)
464a75b2cacSAlex Lorenz return 1;
465a75b2cacSAlex Lorenz diff::SyntaxTree Tree(AST->getASTContext());
466a1d2b5d5SJohannes Altmanninger if (ASTDump) {
467a1d2b5d5SJohannes Altmanninger printTree(llvm::outs(), Tree);
468a1d2b5d5SJohannes Altmanninger return 0;
469a1d2b5d5SJohannes Altmanninger }
4700da12c84SJohannes Altmanninger llvm::outs() << R"({"filename":")";
4710da12c84SJohannes Altmanninger printJsonString(llvm::outs(), SourcePath);
4720da12c84SJohannes Altmanninger llvm::outs() << R"(","root":)";
4730da12c84SJohannes Altmanninger printNodeAsJson(llvm::outs(), Tree, Tree.getRootId());
4740da12c84SJohannes Altmanninger llvm::outs() << "}\n";
475a75b2cacSAlex Lorenz return 0;
476a75b2cacSAlex Lorenz }
477a75b2cacSAlex Lorenz
478a75b2cacSAlex Lorenz if (DestinationPath.empty()) {
479a75b2cacSAlex Lorenz llvm::errs() << "Error: Exactly two paths are required.\n";
480a75b2cacSAlex Lorenz return 1;
481a75b2cacSAlex Lorenz }
482a75b2cacSAlex Lorenz
483849f20e4SJohannes Altmanninger std::unique_ptr<ASTUnit> Src = getAST(CommonCompilations, SourcePath);
484849f20e4SJohannes Altmanninger std::unique_ptr<ASTUnit> Dst = getAST(CommonCompilations, DestinationPath);
485a75b2cacSAlex Lorenz if (!Src || !Dst)
486a75b2cacSAlex Lorenz return 1;
487a75b2cacSAlex Lorenz
488a75b2cacSAlex Lorenz diff::ComparisonOptions Options;
489849f20e4SJohannes Altmanninger if (MaxSize != -1)
490849f20e4SJohannes Altmanninger Options.MaxSize = MaxSize;
491d1969307SJohannes Altmanninger if (!StopAfter.empty()) {
492d1969307SJohannes Altmanninger if (StopAfter == "topdown")
493d1969307SJohannes Altmanninger Options.StopAfterTopDown = true;
494d1969307SJohannes Altmanninger else if (StopAfter != "bottomup") {
495d1969307SJohannes Altmanninger llvm::errs() << "Error: Invalid argument for -stop-after\n";
496d1969307SJohannes Altmanninger return 1;
497d1969307SJohannes Altmanninger }
498d1969307SJohannes Altmanninger }
499a75b2cacSAlex Lorenz diff::SyntaxTree SrcTree(Src->getASTContext());
500a75b2cacSAlex Lorenz diff::SyntaxTree DstTree(Dst->getASTContext());
501e0fe5cd4SJohannes Altmanninger diff::ASTDiff Diff(SrcTree, DstTree, Options);
502e0fe5cd4SJohannes Altmanninger
503a29d6aecSJohannes Altmanninger if (HtmlDiff) {
504a29d6aecSJohannes Altmanninger llvm::outs() << HtmlDiffHeader << "<pre>";
505a29d6aecSJohannes Altmanninger llvm::outs() << "<div id='L' class='code'>";
506a29d6aecSJohannes Altmanninger printHtmlForNode(llvm::outs(), Diff, SrcTree, true, SrcTree.getRootId(), 0);
507a29d6aecSJohannes Altmanninger llvm::outs() << "</div>";
508a29d6aecSJohannes Altmanninger llvm::outs() << "<div id='R' class='code'>";
509a29d6aecSJohannes Altmanninger printHtmlForNode(llvm::outs(), Diff, DstTree, false, DstTree.getRootId(),
510a29d6aecSJohannes Altmanninger 0);
511a29d6aecSJohannes Altmanninger llvm::outs() << "</div>";
512a29d6aecSJohannes Altmanninger llvm::outs() << "</pre></div></body></html>\n";
513a29d6aecSJohannes Altmanninger return 0;
514a29d6aecSJohannes Altmanninger }
515a29d6aecSJohannes Altmanninger
516e0fe5cd4SJohannes Altmanninger for (diff::NodeId Dst : DstTree) {
517e0fe5cd4SJohannes Altmanninger diff::NodeId Src = Diff.getMapped(DstTree, Dst);
518683876caSJohannes Altmanninger if (PrintMatches && Src.isValid()) {
519e0fe5cd4SJohannes Altmanninger llvm::outs() << "Match ";
520e0fe5cd4SJohannes Altmanninger printNode(llvm::outs(), SrcTree, Src);
521e0fe5cd4SJohannes Altmanninger llvm::outs() << " to ";
522e0fe5cd4SJohannes Altmanninger printNode(llvm::outs(), DstTree, Dst);
523e0fe5cd4SJohannes Altmanninger llvm::outs() << "\n";
524e0fe5cd4SJohannes Altmanninger }
525e0fe5cd4SJohannes Altmanninger printDstChange(llvm::outs(), Diff, SrcTree, DstTree, Dst);
526e0fe5cd4SJohannes Altmanninger }
527e0fe5cd4SJohannes Altmanninger for (diff::NodeId Src : SrcTree) {
528e0fe5cd4SJohannes Altmanninger if (Diff.getMapped(SrcTree, Src).isInvalid()) {
529e0fe5cd4SJohannes Altmanninger llvm::outs() << "Delete ";
530e0fe5cd4SJohannes Altmanninger printNode(llvm::outs(), SrcTree, Src);
531e0fe5cd4SJohannes Altmanninger llvm::outs() << "\n";
532e0fe5cd4SJohannes Altmanninger }
533e0fe5cd4SJohannes Altmanninger }
534a75b2cacSAlex Lorenz
535a75b2cacSAlex Lorenz return 0;
536a75b2cacSAlex Lorenz }
537