Home | History | Annotate | Line # | Download | only in clang-diff
      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 << "&amp;";
    225      1.1  joerg     break;
    226      1.1  joerg   case '<':
    227      1.1  joerg     OS << "&lt;";
    228      1.1  joerg     break;
    229      1.1  joerg   case '>':
    230      1.1  joerg     OS << "&gt;";
    231      1.1  joerg     break;
    232      1.1  joerg   case '\'':
    233      1.1  joerg     OS << "&#x27;";
    234      1.1  joerg     break;
    235      1.1  joerg   case '"':
    236      1.1  joerg     OS << "&quot;";
    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