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