17330f729Sjoerg //===- ClangDiff.cpp - compare source files by AST nodes ------*- C++ -*- -===//
27330f729Sjoerg //
37330f729Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
47330f729Sjoerg // See https://llvm.org/LICENSE.txt for license information.
57330f729Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67330f729Sjoerg //
77330f729Sjoerg //===----------------------------------------------------------------------===//
87330f729Sjoerg //
97330f729Sjoerg // This file implements a tool for syntax tree based comparison using
107330f729Sjoerg // Tooling/ASTDiff.
117330f729Sjoerg //
127330f729Sjoerg //===----------------------------------------------------------------------===//
137330f729Sjoerg
147330f729Sjoerg #include "clang/Tooling/ASTDiff/ASTDiff.h"
157330f729Sjoerg #include "clang/Tooling/CommonOptionsParser.h"
167330f729Sjoerg #include "clang/Tooling/Tooling.h"
177330f729Sjoerg #include "llvm/Support/CommandLine.h"
187330f729Sjoerg
197330f729Sjoerg using namespace llvm;
207330f729Sjoerg using namespace clang;
217330f729Sjoerg using namespace clang::tooling;
227330f729Sjoerg
237330f729Sjoerg static cl::OptionCategory ClangDiffCategory("clang-diff options");
247330f729Sjoerg
257330f729Sjoerg static cl::opt<bool>
267330f729Sjoerg ASTDump("ast-dump",
277330f729Sjoerg cl::desc("Print the internal representation of the AST."),
287330f729Sjoerg cl::init(false), cl::cat(ClangDiffCategory));
297330f729Sjoerg
307330f729Sjoerg static cl::opt<bool> ASTDumpJson(
317330f729Sjoerg "ast-dump-json",
327330f729Sjoerg cl::desc("Print the internal representation of the AST as JSON."),
337330f729Sjoerg cl::init(false), cl::cat(ClangDiffCategory));
347330f729Sjoerg
357330f729Sjoerg static cl::opt<bool> PrintMatches("dump-matches",
367330f729Sjoerg cl::desc("Print the matched nodes."),
377330f729Sjoerg cl::init(false), cl::cat(ClangDiffCategory));
387330f729Sjoerg
397330f729Sjoerg static cl::opt<bool> HtmlDiff("html",
407330f729Sjoerg cl::desc("Output a side-by-side diff in HTML."),
417330f729Sjoerg cl::init(false), cl::cat(ClangDiffCategory));
427330f729Sjoerg
437330f729Sjoerg static cl::opt<std::string> SourcePath(cl::Positional, cl::desc("<source>"),
447330f729Sjoerg cl::Required,
457330f729Sjoerg cl::cat(ClangDiffCategory));
467330f729Sjoerg
477330f729Sjoerg static cl::opt<std::string> DestinationPath(cl::Positional,
487330f729Sjoerg cl::desc("<destination>"),
497330f729Sjoerg cl::Optional,
507330f729Sjoerg cl::cat(ClangDiffCategory));
517330f729Sjoerg
527330f729Sjoerg static cl::opt<std::string> StopAfter("stop-diff-after",
537330f729Sjoerg cl::desc("<topdown|bottomup>"),
547330f729Sjoerg cl::Optional, cl::init(""),
557330f729Sjoerg cl::cat(ClangDiffCategory));
567330f729Sjoerg
577330f729Sjoerg static cl::opt<int> MaxSize("s", cl::desc("<maxsize>"), cl::Optional,
587330f729Sjoerg cl::init(-1), cl::cat(ClangDiffCategory));
597330f729Sjoerg
607330f729Sjoerg static cl::opt<std::string> BuildPath("p", cl::desc("Build path"), cl::init(""),
617330f729Sjoerg cl::Optional, cl::cat(ClangDiffCategory));
627330f729Sjoerg
637330f729Sjoerg static cl::list<std::string> ArgsAfter(
647330f729Sjoerg "extra-arg",
657330f729Sjoerg cl::desc("Additional argument to append to the compiler command line"),
667330f729Sjoerg cl::cat(ClangDiffCategory));
677330f729Sjoerg
687330f729Sjoerg static cl::list<std::string> ArgsBefore(
697330f729Sjoerg "extra-arg-before",
707330f729Sjoerg cl::desc("Additional argument to prepend to the compiler command line"),
717330f729Sjoerg cl::cat(ClangDiffCategory));
727330f729Sjoerg
addExtraArgs(std::unique_ptr<CompilationDatabase> & Compilations)737330f729Sjoerg static void addExtraArgs(std::unique_ptr<CompilationDatabase> &Compilations) {
747330f729Sjoerg if (!Compilations)
757330f729Sjoerg return;
767330f729Sjoerg auto AdjustingCompilations =
777330f729Sjoerg std::make_unique<ArgumentsAdjustingCompilations>(
787330f729Sjoerg std::move(Compilations));
797330f729Sjoerg AdjustingCompilations->appendArgumentsAdjuster(
807330f729Sjoerg getInsertArgumentAdjuster(ArgsBefore, ArgumentInsertPosition::BEGIN));
817330f729Sjoerg AdjustingCompilations->appendArgumentsAdjuster(
827330f729Sjoerg getInsertArgumentAdjuster(ArgsAfter, ArgumentInsertPosition::END));
837330f729Sjoerg Compilations = std::move(AdjustingCompilations);
847330f729Sjoerg }
857330f729Sjoerg
867330f729Sjoerg static std::unique_ptr<ASTUnit>
getAST(const std::unique_ptr<CompilationDatabase> & CommonCompilations,const StringRef Filename)877330f729Sjoerg getAST(const std::unique_ptr<CompilationDatabase> &CommonCompilations,
887330f729Sjoerg const StringRef Filename) {
897330f729Sjoerg std::string ErrorMessage;
907330f729Sjoerg std::unique_ptr<CompilationDatabase> Compilations;
917330f729Sjoerg if (!CommonCompilations) {
927330f729Sjoerg Compilations = CompilationDatabase::autoDetectFromSource(
937330f729Sjoerg BuildPath.empty() ? Filename : BuildPath, ErrorMessage);
947330f729Sjoerg if (!Compilations) {
957330f729Sjoerg llvm::errs()
967330f729Sjoerg << "Error while trying to load a compilation database, running "
977330f729Sjoerg "without flags.\n"
987330f729Sjoerg << ErrorMessage;
997330f729Sjoerg Compilations =
1007330f729Sjoerg std::make_unique<clang::tooling::FixedCompilationDatabase>(
1017330f729Sjoerg ".", std::vector<std::string>());
1027330f729Sjoerg }
1037330f729Sjoerg }
1047330f729Sjoerg addExtraArgs(Compilations);
105*e038c9c4Sjoerg std::array<std::string, 1> Files = {{std::string(Filename)}};
1067330f729Sjoerg ClangTool Tool(Compilations ? *Compilations : *CommonCompilations, Files);
1077330f729Sjoerg std::vector<std::unique_ptr<ASTUnit>> ASTs;
1087330f729Sjoerg Tool.buildASTs(ASTs);
1097330f729Sjoerg if (ASTs.size() != Files.size())
1107330f729Sjoerg return nullptr;
1117330f729Sjoerg return std::move(ASTs[0]);
1127330f729Sjoerg }
1137330f729Sjoerg
hexdigit(int N)1147330f729Sjoerg static char hexdigit(int N) { return N &= 0xf, N + (N < 10 ? '0' : 'a' - 10); }
1157330f729Sjoerg
1167330f729Sjoerg static const char HtmlDiffHeader[] = R"(
1177330f729Sjoerg <html>
1187330f729Sjoerg <head>
1197330f729Sjoerg <meta charset='utf-8'/>
1207330f729Sjoerg <style>
1217330f729Sjoerg span.d { color: red; }
1227330f729Sjoerg span.u { color: #cc00cc; }
1237330f729Sjoerg span.i { color: green; }
1247330f729Sjoerg span.m { font-weight: bold; }
1257330f729Sjoerg span { font-weight: normal; color: black; }
1267330f729Sjoerg div.code {
1277330f729Sjoerg width: 48%;
1287330f729Sjoerg height: 98%;
1297330f729Sjoerg overflow: scroll;
1307330f729Sjoerg float: left;
1317330f729Sjoerg padding: 0 0 0.5% 0.5%;
1327330f729Sjoerg border: solid 2px LightGrey;
1337330f729Sjoerg border-radius: 5px;
1347330f729Sjoerg }
1357330f729Sjoerg </style>
1367330f729Sjoerg </head>
1377330f729Sjoerg <script type='text/javascript'>
1387330f729Sjoerg highlightStack = []
1397330f729Sjoerg function clearHighlight() {
1407330f729Sjoerg while (highlightStack.length) {
1417330f729Sjoerg var [l, r] = highlightStack.pop()
1427330f729Sjoerg document.getElementById(l).style.backgroundColor = 'inherit'
1437330f729Sjoerg if (r[1] != '-')
1447330f729Sjoerg document.getElementById(r).style.backgroundColor = 'inherit'
1457330f729Sjoerg }
1467330f729Sjoerg }
1477330f729Sjoerg function highlight(event) {
1487330f729Sjoerg var id = event.target['id']
1497330f729Sjoerg doHighlight(id)
1507330f729Sjoerg }
1517330f729Sjoerg function doHighlight(id) {
1527330f729Sjoerg clearHighlight()
1537330f729Sjoerg source = document.getElementById(id)
1547330f729Sjoerg if (!source.attributes['tid'])
1557330f729Sjoerg return
1567330f729Sjoerg var mapped = source
1577330f729Sjoerg while (mapped && mapped.parentElement && mapped.attributes['tid'].value.substr(1) === '-1')
1587330f729Sjoerg mapped = mapped.parentElement
1597330f729Sjoerg var tid = null, target = null
1607330f729Sjoerg if (mapped) {
1617330f729Sjoerg tid = mapped.attributes['tid'].value
1627330f729Sjoerg target = document.getElementById(tid)
1637330f729Sjoerg }
1647330f729Sjoerg if (source.parentElement && source.parentElement.classList.contains('code'))
1657330f729Sjoerg return
1667330f729Sjoerg source.style.backgroundColor = 'lightgrey'
1677330f729Sjoerg source.scrollIntoView()
1687330f729Sjoerg if (target) {
1697330f729Sjoerg if (mapped === source)
1707330f729Sjoerg target.style.backgroundColor = 'lightgrey'
1717330f729Sjoerg target.scrollIntoView()
1727330f729Sjoerg }
1737330f729Sjoerg highlightStack.push([id, tid])
1747330f729Sjoerg location.hash = '#' + id
1757330f729Sjoerg }
1767330f729Sjoerg function scrollToBoth() {
1777330f729Sjoerg doHighlight(location.hash.substr(1))
1787330f729Sjoerg }
1797330f729Sjoerg function changed(elem) {
1807330f729Sjoerg return elem.classList.length == 0
1817330f729Sjoerg }
1827330f729Sjoerg function nextChangedNode(prefix, increment, number) {
1837330f729Sjoerg do {
1847330f729Sjoerg number += increment
1857330f729Sjoerg var elem = document.getElementById(prefix + number)
1867330f729Sjoerg } while(elem && !changed(elem))
1877330f729Sjoerg return elem ? number : null
1887330f729Sjoerg }
1897330f729Sjoerg function handleKey(e) {
1907330f729Sjoerg var down = e.code === "KeyJ"
1917330f729Sjoerg var up = e.code === "KeyK"
1927330f729Sjoerg if (!down && !up)
1937330f729Sjoerg return
1947330f729Sjoerg var id = highlightStack[0] ? highlightStack[0][0] : 'R0'
1957330f729Sjoerg var oldelem = document.getElementById(id)
1967330f729Sjoerg var number = parseInt(id.substr(1))
1977330f729Sjoerg var increment = down ? 1 : -1
1987330f729Sjoerg var lastnumber = number
1997330f729Sjoerg var prefix = id[0]
2007330f729Sjoerg do {
2017330f729Sjoerg number = nextChangedNode(prefix, increment, number)
2027330f729Sjoerg var elem = document.getElementById(prefix + number)
2037330f729Sjoerg if (up && elem) {
2047330f729Sjoerg while (elem.parentElement && changed(elem.parentElement))
2057330f729Sjoerg elem = elem.parentElement
2067330f729Sjoerg number = elem.id.substr(1)
2077330f729Sjoerg }
2087330f729Sjoerg } while ((down && id !== 'R0' && oldelem.contains(elem)))
2097330f729Sjoerg if (!number)
2107330f729Sjoerg number = lastnumber
2117330f729Sjoerg elem = document.getElementById(prefix + number)
2127330f729Sjoerg doHighlight(prefix + number)
2137330f729Sjoerg }
2147330f729Sjoerg window.onload = scrollToBoth
2157330f729Sjoerg window.onkeydown = handleKey
2167330f729Sjoerg </script>
2177330f729Sjoerg <body>
2187330f729Sjoerg <div onclick='highlight(event)'>
2197330f729Sjoerg )";
2207330f729Sjoerg
printHtml(raw_ostream & OS,char C)2217330f729Sjoerg static void printHtml(raw_ostream &OS, char C) {
2227330f729Sjoerg switch (C) {
2237330f729Sjoerg case '&':
2247330f729Sjoerg OS << "&";
2257330f729Sjoerg break;
2267330f729Sjoerg case '<':
2277330f729Sjoerg OS << "<";
2287330f729Sjoerg break;
2297330f729Sjoerg case '>':
2307330f729Sjoerg OS << ">";
2317330f729Sjoerg break;
2327330f729Sjoerg case '\'':
2337330f729Sjoerg OS << "'";
2347330f729Sjoerg break;
2357330f729Sjoerg case '"':
2367330f729Sjoerg OS << """;
2377330f729Sjoerg break;
2387330f729Sjoerg default:
2397330f729Sjoerg OS << C;
2407330f729Sjoerg }
2417330f729Sjoerg }
2427330f729Sjoerg
printHtml(raw_ostream & OS,const StringRef Str)2437330f729Sjoerg static void printHtml(raw_ostream &OS, const StringRef Str) {
2447330f729Sjoerg for (char C : Str)
2457330f729Sjoerg printHtml(OS, C);
2467330f729Sjoerg }
2477330f729Sjoerg
getChangeKindAbbr(diff::ChangeKind Kind)2487330f729Sjoerg static std::string getChangeKindAbbr(diff::ChangeKind Kind) {
2497330f729Sjoerg switch (Kind) {
2507330f729Sjoerg case diff::None:
2517330f729Sjoerg return "";
2527330f729Sjoerg case diff::Delete:
2537330f729Sjoerg return "d";
2547330f729Sjoerg case diff::Update:
2557330f729Sjoerg return "u";
2567330f729Sjoerg case diff::Insert:
2577330f729Sjoerg return "i";
2587330f729Sjoerg case diff::Move:
2597330f729Sjoerg return "m";
2607330f729Sjoerg case diff::UpdateMove:
2617330f729Sjoerg return "u m";
2627330f729Sjoerg }
2637330f729Sjoerg llvm_unreachable("Invalid enumeration value.");
2647330f729Sjoerg }
2657330f729Sjoerg
printHtmlForNode(raw_ostream & OS,const diff::ASTDiff & Diff,diff::SyntaxTree & Tree,bool IsLeft,diff::NodeId Id,unsigned Offset)2667330f729Sjoerg static unsigned printHtmlForNode(raw_ostream &OS, const diff::ASTDiff &Diff,
2677330f729Sjoerg diff::SyntaxTree &Tree, bool IsLeft,
2687330f729Sjoerg diff::NodeId Id, unsigned Offset) {
2697330f729Sjoerg const diff::Node &Node = Tree.getNode(Id);
2707330f729Sjoerg char MyTag, OtherTag;
2717330f729Sjoerg diff::NodeId LeftId, RightId;
2727330f729Sjoerg diff::NodeId TargetId = Diff.getMapped(Tree, Id);
2737330f729Sjoerg if (IsLeft) {
2747330f729Sjoerg MyTag = 'L';
2757330f729Sjoerg OtherTag = 'R';
2767330f729Sjoerg LeftId = Id;
2777330f729Sjoerg RightId = TargetId;
2787330f729Sjoerg } else {
2797330f729Sjoerg MyTag = 'R';
2807330f729Sjoerg OtherTag = 'L';
2817330f729Sjoerg LeftId = TargetId;
2827330f729Sjoerg RightId = Id;
2837330f729Sjoerg }
2847330f729Sjoerg unsigned Begin, End;
2857330f729Sjoerg std::tie(Begin, End) = Tree.getSourceRangeOffsets(Node);
2867330f729Sjoerg const SourceManager &SrcMgr = Tree.getASTContext().getSourceManager();
287*e038c9c4Sjoerg auto Code = SrcMgr.getBufferOrFake(SrcMgr.getMainFileID()).getBuffer();
2887330f729Sjoerg for (; Offset < Begin; ++Offset)
2897330f729Sjoerg printHtml(OS, Code[Offset]);
2907330f729Sjoerg OS << "<span id='" << MyTag << Id << "' "
2917330f729Sjoerg << "tid='" << OtherTag << TargetId << "' ";
2927330f729Sjoerg OS << "title='";
2937330f729Sjoerg printHtml(OS, Node.getTypeLabel());
2947330f729Sjoerg OS << "\n" << LeftId << " -> " << RightId;
2957330f729Sjoerg std::string Value = Tree.getNodeValue(Node);
2967330f729Sjoerg if (!Value.empty()) {
2977330f729Sjoerg OS << "\n";
2987330f729Sjoerg printHtml(OS, Value);
2997330f729Sjoerg }
3007330f729Sjoerg OS << "'";
3017330f729Sjoerg if (Node.Change != diff::None)
3027330f729Sjoerg OS << " class='" << getChangeKindAbbr(Node.Change) << "'";
3037330f729Sjoerg OS << ">";
3047330f729Sjoerg
3057330f729Sjoerg for (diff::NodeId Child : Node.Children)
3067330f729Sjoerg Offset = printHtmlForNode(OS, Diff, Tree, IsLeft, Child, Offset);
3077330f729Sjoerg
3087330f729Sjoerg for (; Offset < End; ++Offset)
3097330f729Sjoerg printHtml(OS, Code[Offset]);
3107330f729Sjoerg if (Id == Tree.getRootId()) {
3117330f729Sjoerg End = Code.size();
3127330f729Sjoerg for (; Offset < End; ++Offset)
3137330f729Sjoerg printHtml(OS, Code[Offset]);
3147330f729Sjoerg }
3157330f729Sjoerg OS << "</span>";
3167330f729Sjoerg return Offset;
3177330f729Sjoerg }
3187330f729Sjoerg
printJsonString(raw_ostream & OS,const StringRef Str)3197330f729Sjoerg static void printJsonString(raw_ostream &OS, const StringRef Str) {
3207330f729Sjoerg for (signed char C : Str) {
3217330f729Sjoerg switch (C) {
3227330f729Sjoerg case '"':
3237330f729Sjoerg OS << R"(\")";
3247330f729Sjoerg break;
3257330f729Sjoerg case '\\':
3267330f729Sjoerg OS << R"(\\)";
3277330f729Sjoerg break;
3287330f729Sjoerg case '\n':
3297330f729Sjoerg OS << R"(\n)";
3307330f729Sjoerg break;
3317330f729Sjoerg case '\t':
3327330f729Sjoerg OS << R"(\t)";
3337330f729Sjoerg break;
3347330f729Sjoerg default:
3357330f729Sjoerg if ('\x00' <= C && C <= '\x1f') {
3367330f729Sjoerg OS << R"(\u00)" << hexdigit(C >> 4) << hexdigit(C);
3377330f729Sjoerg } else {
3387330f729Sjoerg OS << C;
3397330f729Sjoerg }
3407330f729Sjoerg }
3417330f729Sjoerg }
3427330f729Sjoerg }
3437330f729Sjoerg
printNodeAttributes(raw_ostream & OS,diff::SyntaxTree & Tree,diff::NodeId Id)3447330f729Sjoerg static void printNodeAttributes(raw_ostream &OS, diff::SyntaxTree &Tree,
3457330f729Sjoerg diff::NodeId Id) {
3467330f729Sjoerg const diff::Node &N = Tree.getNode(Id);
3477330f729Sjoerg OS << R"("id":)" << int(Id);
3487330f729Sjoerg OS << R"(,"type":")" << N.getTypeLabel() << '"';
3497330f729Sjoerg auto Offsets = Tree.getSourceRangeOffsets(N);
3507330f729Sjoerg OS << R"(,"begin":)" << Offsets.first;
3517330f729Sjoerg OS << R"(,"end":)" << Offsets.second;
3527330f729Sjoerg std::string Value = Tree.getNodeValue(N);
3537330f729Sjoerg if (!Value.empty()) {
3547330f729Sjoerg OS << R"(,"value":")";
3557330f729Sjoerg printJsonString(OS, Value);
3567330f729Sjoerg OS << '"';
3577330f729Sjoerg }
3587330f729Sjoerg }
3597330f729Sjoerg
3607330f729Sjoerg static void printNodeAsJson(raw_ostream &OS, diff::SyntaxTree &Tree,
3617330f729Sjoerg diff::NodeId Id) {
3627330f729Sjoerg const diff::Node &N = Tree.getNode(Id);
3637330f729Sjoerg OS << "{";
3647330f729Sjoerg printNodeAttributes(OS, Tree, Id);
3657330f729Sjoerg auto Identifier = N.getIdentifier();
3667330f729Sjoerg auto QualifiedIdentifier = N.getQualifiedIdentifier();
3677330f729Sjoerg if (Identifier) {
3687330f729Sjoerg OS << R"(,"identifier":")";
3697330f729Sjoerg printJsonString(OS, *Identifier);
3707330f729Sjoerg OS << R"(")";
3717330f729Sjoerg if (QualifiedIdentifier && *Identifier != *QualifiedIdentifier) {
3727330f729Sjoerg OS << R"(,"qualified_identifier":")";
3737330f729Sjoerg printJsonString(OS, *QualifiedIdentifier);
3747330f729Sjoerg OS << R"(")";
3757330f729Sjoerg }
3767330f729Sjoerg }
3777330f729Sjoerg OS << R"(,"children":[)";
3787330f729Sjoerg if (N.Children.size() > 0) {
3797330f729Sjoerg printNodeAsJson(OS, Tree, N.Children[0]);
3807330f729Sjoerg for (size_t I = 1, E = N.Children.size(); I < E; ++I) {
3817330f729Sjoerg OS << ",";
3827330f729Sjoerg printNodeAsJson(OS, Tree, N.Children[I]);
3837330f729Sjoerg }
3847330f729Sjoerg }
3857330f729Sjoerg OS << "]}";
3867330f729Sjoerg }
3877330f729Sjoerg
3887330f729Sjoerg static void printNode(raw_ostream &OS, diff::SyntaxTree &Tree,
3897330f729Sjoerg diff::NodeId Id) {
3907330f729Sjoerg if (Id.isInvalid()) {
3917330f729Sjoerg OS << "None";
3927330f729Sjoerg return;
3937330f729Sjoerg }
3947330f729Sjoerg OS << Tree.getNode(Id).getTypeLabel();
3957330f729Sjoerg std::string Value = Tree.getNodeValue(Id);
3967330f729Sjoerg if (!Value.empty())
3977330f729Sjoerg OS << ": " << Value;
3987330f729Sjoerg OS << "(" << Id << ")";
3997330f729Sjoerg }
4007330f729Sjoerg
4017330f729Sjoerg static void printTree(raw_ostream &OS, diff::SyntaxTree &Tree) {
4027330f729Sjoerg for (diff::NodeId Id : Tree) {
4037330f729Sjoerg for (int I = 0; I < Tree.getNode(Id).Depth; ++I)
4047330f729Sjoerg OS << " ";
4057330f729Sjoerg printNode(OS, Tree, Id);
4067330f729Sjoerg OS << "\n";
4077330f729Sjoerg }
4087330f729Sjoerg }
4097330f729Sjoerg
4107330f729Sjoerg static void printDstChange(raw_ostream &OS, diff::ASTDiff &Diff,
4117330f729Sjoerg diff::SyntaxTree &SrcTree, diff::SyntaxTree &DstTree,
4127330f729Sjoerg diff::NodeId Dst) {
4137330f729Sjoerg const diff::Node &DstNode = DstTree.getNode(Dst);
4147330f729Sjoerg diff::NodeId Src = Diff.getMapped(DstTree, Dst);
4157330f729Sjoerg switch (DstNode.Change) {
4167330f729Sjoerg case diff::None:
4177330f729Sjoerg break;
4187330f729Sjoerg case diff::Delete:
4197330f729Sjoerg llvm_unreachable("The destination tree can't have deletions.");
4207330f729Sjoerg case diff::Update:
4217330f729Sjoerg OS << "Update ";
4227330f729Sjoerg printNode(OS, SrcTree, Src);
4237330f729Sjoerg OS << " to " << DstTree.getNodeValue(Dst) << "\n";
4247330f729Sjoerg break;
4257330f729Sjoerg case diff::Insert:
4267330f729Sjoerg case diff::Move:
4277330f729Sjoerg case diff::UpdateMove:
4287330f729Sjoerg if (DstNode.Change == diff::Insert)
4297330f729Sjoerg OS << "Insert";
4307330f729Sjoerg else if (DstNode.Change == diff::Move)
4317330f729Sjoerg OS << "Move";
4327330f729Sjoerg else if (DstNode.Change == diff::UpdateMove)
4337330f729Sjoerg OS << "Update and Move";
4347330f729Sjoerg OS << " ";
4357330f729Sjoerg printNode(OS, DstTree, Dst);
4367330f729Sjoerg OS << " into ";
4377330f729Sjoerg printNode(OS, DstTree, DstNode.Parent);
4387330f729Sjoerg OS << " at " << DstTree.findPositionInParent(Dst) << "\n";
4397330f729Sjoerg break;
4407330f729Sjoerg }
4417330f729Sjoerg }
4427330f729Sjoerg
4437330f729Sjoerg int main(int argc, const char **argv) {
4447330f729Sjoerg std::string ErrorMessage;
4457330f729Sjoerg std::unique_ptr<CompilationDatabase> CommonCompilations =
4467330f729Sjoerg FixedCompilationDatabase::loadFromCommandLine(argc, argv, ErrorMessage);
4477330f729Sjoerg if (!CommonCompilations && !ErrorMessage.empty())
4487330f729Sjoerg llvm::errs() << ErrorMessage;
4497330f729Sjoerg cl::HideUnrelatedOptions(ClangDiffCategory);
4507330f729Sjoerg if (!cl::ParseCommandLineOptions(argc, argv)) {
4517330f729Sjoerg cl::PrintOptionValues();
4527330f729Sjoerg return 1;
4537330f729Sjoerg }
4547330f729Sjoerg
4557330f729Sjoerg addExtraArgs(CommonCompilations);
4567330f729Sjoerg
4577330f729Sjoerg if (ASTDump || ASTDumpJson) {
4587330f729Sjoerg if (!DestinationPath.empty()) {
4597330f729Sjoerg llvm::errs() << "Error: Please specify exactly one filename.\n";
4607330f729Sjoerg return 1;
4617330f729Sjoerg }
4627330f729Sjoerg std::unique_ptr<ASTUnit> AST = getAST(CommonCompilations, SourcePath);
4637330f729Sjoerg if (!AST)
4647330f729Sjoerg return 1;
4657330f729Sjoerg diff::SyntaxTree Tree(AST->getASTContext());
4667330f729Sjoerg if (ASTDump) {
4677330f729Sjoerg printTree(llvm::outs(), Tree);
4687330f729Sjoerg return 0;
4697330f729Sjoerg }
4707330f729Sjoerg llvm::outs() << R"({"filename":")";
4717330f729Sjoerg printJsonString(llvm::outs(), SourcePath);
4727330f729Sjoerg llvm::outs() << R"(","root":)";
4737330f729Sjoerg printNodeAsJson(llvm::outs(), Tree, Tree.getRootId());
4747330f729Sjoerg llvm::outs() << "}\n";
4757330f729Sjoerg return 0;
4767330f729Sjoerg }
4777330f729Sjoerg
4787330f729Sjoerg if (DestinationPath.empty()) {
4797330f729Sjoerg llvm::errs() << "Error: Exactly two paths are required.\n";
4807330f729Sjoerg return 1;
4817330f729Sjoerg }
4827330f729Sjoerg
4837330f729Sjoerg std::unique_ptr<ASTUnit> Src = getAST(CommonCompilations, SourcePath);
4847330f729Sjoerg std::unique_ptr<ASTUnit> Dst = getAST(CommonCompilations, DestinationPath);
4857330f729Sjoerg if (!Src || !Dst)
4867330f729Sjoerg return 1;
4877330f729Sjoerg
4887330f729Sjoerg diff::ComparisonOptions Options;
4897330f729Sjoerg if (MaxSize != -1)
4907330f729Sjoerg Options.MaxSize = MaxSize;
4917330f729Sjoerg if (!StopAfter.empty()) {
4927330f729Sjoerg if (StopAfter == "topdown")
4937330f729Sjoerg Options.StopAfterTopDown = true;
4947330f729Sjoerg else if (StopAfter != "bottomup") {
4957330f729Sjoerg llvm::errs() << "Error: Invalid argument for -stop-after\n";
4967330f729Sjoerg return 1;
4977330f729Sjoerg }
4987330f729Sjoerg }
4997330f729Sjoerg diff::SyntaxTree SrcTree(Src->getASTContext());
5007330f729Sjoerg diff::SyntaxTree DstTree(Dst->getASTContext());
5017330f729Sjoerg diff::ASTDiff Diff(SrcTree, DstTree, Options);
5027330f729Sjoerg
5037330f729Sjoerg if (HtmlDiff) {
5047330f729Sjoerg llvm::outs() << HtmlDiffHeader << "<pre>";
5057330f729Sjoerg llvm::outs() << "<div id='L' class='code'>";
5067330f729Sjoerg printHtmlForNode(llvm::outs(), Diff, SrcTree, true, SrcTree.getRootId(), 0);
5077330f729Sjoerg llvm::outs() << "</div>";
5087330f729Sjoerg llvm::outs() << "<div id='R' class='code'>";
5097330f729Sjoerg printHtmlForNode(llvm::outs(), Diff, DstTree, false, DstTree.getRootId(),
5107330f729Sjoerg 0);
5117330f729Sjoerg llvm::outs() << "</div>";
5127330f729Sjoerg llvm::outs() << "</pre></div></body></html>\n";
5137330f729Sjoerg return 0;
5147330f729Sjoerg }
5157330f729Sjoerg
5167330f729Sjoerg for (diff::NodeId Dst : DstTree) {
5177330f729Sjoerg diff::NodeId Src = Diff.getMapped(DstTree, Dst);
5187330f729Sjoerg if (PrintMatches && Src.isValid()) {
5197330f729Sjoerg llvm::outs() << "Match ";
5207330f729Sjoerg printNode(llvm::outs(), SrcTree, Src);
5217330f729Sjoerg llvm::outs() << " to ";
5227330f729Sjoerg printNode(llvm::outs(), DstTree, Dst);
5237330f729Sjoerg llvm::outs() << "\n";
5247330f729Sjoerg }
5257330f729Sjoerg printDstChange(llvm::outs(), Diff, SrcTree, DstTree, Dst);
5267330f729Sjoerg }
5277330f729Sjoerg for (diff::NodeId Src : SrcTree) {
5287330f729Sjoerg if (Diff.getMapped(SrcTree, Src).isInvalid()) {
5297330f729Sjoerg llvm::outs() << "Delete ";
5307330f729Sjoerg printNode(llvm::outs(), SrcTree, Src);
5317330f729Sjoerg llvm::outs() << "\n";
5327330f729Sjoerg }
5337330f729Sjoerg }
5347330f729Sjoerg
5357330f729Sjoerg return 0;
5367330f729Sjoerg }
537