1a75b2cacSAlex Lorenz //===- ClangDiff.cpp - compare source files by AST nodes ------*- C++ -*- -===// 2a75b2cacSAlex Lorenz // 3*2946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*2946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information. 5*2946cd70SChandler 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 73849f20e4SJohannes Altmanninger static void addExtraArgs(std::unique_ptr<CompilationDatabase> &Compilations) { 74849f20e4SJohannes Altmanninger if (!Compilations) 75849f20e4SJohannes Altmanninger return; 76849f20e4SJohannes Altmanninger auto AdjustingCompilations = 77849f20e4SJohannes Altmanninger llvm::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> 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 = 100849f20e4SJohannes Altmanninger llvm::make_unique<clang::tooling::FixedCompilationDatabase>( 101a75b2cacSAlex Lorenz ".", std::vector<std::string>()); 102a75b2cacSAlex Lorenz } 103849f20e4SJohannes Altmanninger } 104849f20e4SJohannes Altmanninger addExtraArgs(Compilations); 105a75b2cacSAlex Lorenz std::array<std::string, 1> Files = {{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 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 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 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 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 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(); 287a29d6aecSJohannes Altmanninger auto Code = SrcMgr.getBuffer(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 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 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