1 //===-- DiffEngine.cpp - Structural file comparison -----------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file defines the implementation of the llvm-tapi difference 10 // engine, which structurally compares two tbd files. 11 // 12 //===----------------------------------------------------------------------===/ 13 #include "DiffEngine.h" 14 #include "llvm/ADT/SmallString.h" 15 #include "llvm/Support/Casting.h" 16 #include "llvm/Support/raw_ostream.h" 17 #include "llvm/TextAPI/InterfaceFile.h" 18 #include "llvm/TextAPI/Symbol.h" 19 #include "llvm/TextAPI/Target.h" 20 #include <iterator> 21 22 using namespace llvm; 23 using namespace MachO; 24 using namespace object; 25 26 StringRef setOrderIndicator(InterfaceInputOrder Order) { 27 return ((Order == lhs) ? "< " : "> "); 28 } 29 30 // The following template specialization implementations 31 // need to be explicitly placed into the llvm namespace 32 // to work around a GCC 4.8 bug. 33 namespace llvm { 34 35 template <typename T, DiffAttrKind U> 36 inline void DiffScalarVal<T, U>::print(raw_ostream &OS, std::string Indent) { 37 OS << Indent << "\t" << setOrderIndicator(Order) << Val << "\n"; 38 } 39 40 template <> 41 inline void 42 DiffScalarVal<StringRef, AD_Diff_Scalar_Str>::print(raw_ostream &OS, 43 std::string Indent) { 44 OS << Indent << "\t\t" << setOrderIndicator(Order) << Val << "\n"; 45 } 46 47 template <> 48 inline void 49 DiffScalarVal<uint8_t, AD_Diff_Scalar_Unsigned>::print(raw_ostream &OS, 50 std::string Indent) { 51 OS << Indent << "\t" << setOrderIndicator(Order) << std::to_string(Val) 52 << "\n"; 53 } 54 55 template <> 56 inline void 57 DiffScalarVal<bool, AD_Diff_Scalar_Bool>::print(raw_ostream &OS, 58 std::string Indent) { 59 OS << Indent << "\t" << setOrderIndicator(Order) 60 << ((Val == true) ? "true" : "false") << "\n"; 61 } 62 63 } // end namespace llvm 64 65 StringLiteral SymScalar::getSymbolNamePrefix(MachO::SymbolKind Kind) { 66 switch (Kind) { 67 case MachO::SymbolKind::GlobalSymbol: 68 return StringLiteral(""); 69 case MachO::SymbolKind::ObjectiveCClass: 70 return ObjC2MetaClassNamePrefix; 71 case MachO::SymbolKind ::ObjectiveCClassEHType: 72 return ObjC2EHTypePrefix; 73 case MachO::SymbolKind ::ObjectiveCInstanceVariable: 74 return ObjC2IVarPrefix; 75 } 76 llvm_unreachable("Unknown llvm::MachO::SymbolKind enum"); 77 } 78 79 std::string SymScalar::getFlagString(const MachO::Symbol *Sym) { 80 if (Sym->getFlags() == SymbolFlags::None) 81 return {}; 82 SmallString<64> Flags(" - "); 83 if (Sym->isThreadLocalValue()) 84 Flags.append("Thread-Local "); 85 if (Sym->isWeakDefined()) 86 Flags.append("Weak-Defined "); 87 if (Sym->isWeakReferenced()) 88 Flags.append("Weak-Referenced "); 89 if (Sym->isUndefined()) 90 Flags.append("Undefined "); 91 if (Sym->isReexported()) 92 Flags.append("Reexported "); 93 if (Sym->isData()) 94 Flags.append("Data "); 95 if (Sym->isText()) 96 Flags.append("Text "); 97 98 return std::string(Flags); 99 } 100 101 void SymScalar::print(raw_ostream &OS, std::string Indent, MachO::Target Targ) { 102 if (Val->getKind() == MachO::SymbolKind::ObjectiveCClass) { 103 if (Targ.Arch == MachO::AK_i386 && Targ.Platform == MachO::PLATFORM_MACOS) { 104 OS << Indent << "\t\t" << ((Order == lhs) ? "< " : "> ") 105 << ObjC1ClassNamePrefix << Val->getName() << getFlagString(Val) 106 << "\n"; 107 return; 108 } 109 OS << Indent << "\t\t" << ((Order == lhs) ? "< " : "> ") 110 << ObjC2ClassNamePrefix << Val->getName() << getFlagString(Val) << "\n"; 111 } 112 OS << Indent << "\t\t" << ((Order == lhs) ? "< " : "> ") 113 << getSymbolNamePrefix(Val->getKind()) << Val->getName() 114 << getFlagString(Val) << "\n"; 115 } 116 117 bool checkSymbolEquality(llvm::MachO::InterfaceFile::const_symbol_range LHS, 118 llvm::MachO::InterfaceFile::const_symbol_range RHS) { 119 if (std::distance(LHS.begin(), LHS.end()) != 120 std::distance(RHS.begin(), RHS.end())) 121 return false; 122 return std::equal(LHS.begin(), LHS.end(), RHS.begin(), 123 [&](auto LHS, auto RHS) { return *LHS == *RHS; }); 124 } 125 126 template <typename TargetVecT, typename ValTypeT, typename V> 127 void addDiffForTargSlice(V Val, Target Targ, DiffOutput &Diff, 128 InterfaceInputOrder Order) { 129 auto TargetVector = llvm::find_if( 130 Diff.Values, [&](const std::unique_ptr<AttributeDiff> &RawTVec) { 131 if (TargetVecT *TVec = dyn_cast<TargetVecT>(RawTVec.get())) 132 return TVec->Targ == Targ; 133 return false; 134 }); 135 if (TargetVector != Diff.Values.end()) { 136 ValTypeT NewVal(Order, Val); 137 cast<TargetVecT>(TargetVector->get())->TargValues.push_back(NewVal); 138 } else { 139 auto NewTargetVec = std::make_unique<TargetVecT>(Targ); 140 ValTypeT NewVal(Order, Val); 141 NewTargetVec->TargValues.push_back(NewVal); 142 Diff.Values.push_back(std::move(NewTargetVec)); 143 } 144 } 145 146 DiffOutput getSingleAttrDiff(const std::vector<InterfaceFileRef> &IRefVec, 147 std::string Name, InterfaceInputOrder Order) { 148 DiffOutput Diff(Name); 149 Diff.Kind = AD_Str_Vec; 150 for (const auto &IRef : IRefVec) 151 for (auto Targ : IRef.targets()) 152 addDiffForTargSlice<DiffStrVec, 153 DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>( 154 IRef.getInstallName(), Targ, Diff, Order); 155 return Diff; 156 } 157 158 DiffOutput 159 getSingleAttrDiff(const std::vector<std::pair<Target, std::string>> &PairVec, 160 std::string Name, InterfaceInputOrder Order) { 161 DiffOutput Diff(Name); 162 Diff.Kind = AD_Str_Vec; 163 for (const auto &Pair : PairVec) 164 addDiffForTargSlice<DiffStrVec, 165 DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>( 166 StringRef(Pair.second), Pair.first, Diff, Order); 167 return Diff; 168 } 169 170 DiffOutput getSingleAttrDiff(InterfaceFile::const_symbol_range SymRange, 171 std::string Name, InterfaceInputOrder Order) { 172 DiffOutput Diff(Name); 173 Diff.Kind = AD_Sym_Vec; 174 for (const auto *Sym : SymRange) 175 for (auto Targ : Sym->targets()) 176 addDiffForTargSlice<DiffSymVec, SymScalar>(Sym, Targ, Diff, Order); 177 return Diff; 178 } 179 180 template <typename T> 181 DiffOutput getSingleAttrDiff(T SingleAttr, std::string Attribute) { 182 DiffOutput Diff(Attribute); 183 Diff.Kind = SingleAttr.getKind(); 184 Diff.Values.push_back(std::make_unique<T>(SingleAttr)); 185 return Diff; 186 } 187 188 template <typename T, DiffAttrKind U> 189 void diffAttribute(std::string Name, std::vector<DiffOutput> &Output, 190 DiffScalarVal<T, U> Attr) { 191 Output.push_back(getSingleAttrDiff(Attr, Name)); 192 } 193 194 template <typename T> 195 void diffAttribute(std::string Name, std::vector<DiffOutput> &Output, 196 const T &Val, InterfaceInputOrder Order) { 197 Output.push_back(getSingleAttrDiff(Val, Name, Order)); 198 } 199 200 std::vector<DiffOutput> getSingleIF(InterfaceFile *Interface, 201 InterfaceInputOrder Order) { 202 std::vector<DiffOutput> Output; 203 diffAttribute("Install Name", Output, 204 DiffScalarVal<StringRef, AD_Diff_Scalar_Str>( 205 Order, Interface->getInstallName())); 206 diffAttribute("Current Version", Output, 207 DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>( 208 Order, Interface->getCurrentVersion())); 209 diffAttribute("Compatibility Version", Output, 210 DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>( 211 Order, Interface->getCompatibilityVersion())); 212 diffAttribute("Swift ABI Version", Output, 213 DiffScalarVal<uint8_t, AD_Diff_Scalar_Unsigned>( 214 Order, Interface->getSwiftABIVersion())); 215 diffAttribute("Two Level Namespace", Output, 216 DiffScalarVal<bool, AD_Diff_Scalar_Bool>( 217 Order, Interface->isTwoLevelNamespace())); 218 diffAttribute("Application Extension Safe", Output, 219 DiffScalarVal<bool, AD_Diff_Scalar_Bool>( 220 Order, Interface->isApplicationExtensionSafe())); 221 diffAttribute("Reexported Libraries", Output, 222 Interface->reexportedLibraries(), Order); 223 diffAttribute("Allowable Clients", Output, Interface->allowableClients(), 224 Order); 225 diffAttribute("Parent Umbrellas", Output, Interface->umbrellas(), Order); 226 diffAttribute("Symbols", Output, Interface->symbols(), Order); 227 for (auto Doc : Interface->documents()) { 228 DiffOutput Documents("Inlined Reexported Frameworks/Libraries"); 229 Documents.Kind = AD_Inline_Doc; 230 Documents.Values.push_back(std::make_unique<InlineDoc>( 231 InlineDoc(Doc->getInstallName(), getSingleIF(Doc.get(), Order)))); 232 Output.push_back(std::move(Documents)); 233 } 234 return Output; 235 } 236 237 void findAndAddDiff(const std::vector<InterfaceFileRef> &CollectedIRefVec, 238 const std::vector<InterfaceFileRef> &LookupIRefVec, 239 DiffOutput &Result, InterfaceInputOrder Order) { 240 Result.Kind = AD_Str_Vec; 241 for (const auto &IRef : CollectedIRefVec) 242 for (auto Targ : IRef.targets()) { 243 auto FoundIRef = llvm::any_of(LookupIRefVec, [&](const auto LIRef) { 244 return llvm::is_contained(LIRef.targets(), Targ) && 245 IRef.getInstallName() == LIRef.getInstallName(); 246 }); 247 if (!FoundIRef) 248 addDiffForTargSlice<DiffStrVec, 249 DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>( 250 IRef.getInstallName(), Targ, Result, Order); 251 } 252 } 253 254 void findAndAddDiff( 255 const std::vector<std::pair<Target, std::string>> &CollectedPairs, 256 const std::vector<std::pair<Target, std::string>> &LookupPairs, 257 DiffOutput &Result, InterfaceInputOrder Order) { 258 Result.Kind = AD_Str_Vec; 259 for (const auto &Pair : CollectedPairs) { 260 auto FoundPair = llvm::find(LookupPairs, Pair); 261 if (FoundPair == LookupPairs.end()) 262 addDiffForTargSlice<DiffStrVec, 263 DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>( 264 StringRef(Pair.second), Pair.first, Result, Order); 265 } 266 } 267 268 void findAndAddDiff(InterfaceFile::const_symbol_range CollectedSyms, 269 InterfaceFile::const_symbol_range LookupSyms, 270 DiffOutput &Result, InterfaceInputOrder Order) { 271 Result.Kind = AD_Sym_Vec; 272 for (const auto *Sym : CollectedSyms) 273 for (const auto Targ : Sym->targets()) { 274 auto FoundSym = llvm::any_of(LookupSyms, [&](const auto LSym) { 275 return (Sym->getName() == LSym->getName() && 276 Sym->getKind() == LSym->getKind() && 277 Sym->getFlags() == LSym->getFlags() && 278 llvm::is_contained(LSym->targets(), Targ)); 279 }); 280 if (!FoundSym) 281 addDiffForTargSlice<DiffSymVec, SymScalar>(Sym, Targ, Result, Order); 282 } 283 } 284 285 template <typename T> 286 DiffOutput recordDifferences(T LHS, T RHS, std::string Attr) { 287 DiffOutput Diff(Attr); 288 if (LHS.getKind() == RHS.getKind()) { 289 Diff.Kind = LHS.getKind(); 290 Diff.Values.push_back(std::make_unique<T>(LHS)); 291 Diff.Values.push_back(std::make_unique<T>(RHS)); 292 } 293 return Diff; 294 } 295 296 template <typename T> 297 DiffOutput recordDifferences(const std::vector<T> &LHS, 298 const std::vector<T> &RHS, std::string Attr) { 299 DiffOutput Diff(Attr); 300 Diff.Kind = AD_Str_Vec; 301 findAndAddDiff(LHS, RHS, Diff, lhs); 302 findAndAddDiff(RHS, LHS, Diff, rhs); 303 return Diff; 304 } 305 306 DiffOutput recordDifferences(llvm::MachO::InterfaceFile::const_symbol_range LHS, 307 llvm::MachO::InterfaceFile::const_symbol_range RHS, 308 std::string Attr) { 309 DiffOutput Diff(Attr); 310 Diff.Kind = AD_Sym_Vec; 311 findAndAddDiff(LHS, RHS, Diff, lhs); 312 findAndAddDiff(RHS, LHS, Diff, rhs); 313 return Diff; 314 } 315 316 std::vector<DiffOutput> 317 DiffEngine::findDifferences(const InterfaceFile *IFLHS, 318 const InterfaceFile *IFRHS) { 319 std::vector<DiffOutput> Output; 320 if (IFLHS->getInstallName() != IFRHS->getInstallName()) 321 Output.push_back(recordDifferences( 322 DiffScalarVal<StringRef, AD_Diff_Scalar_Str>(lhs, 323 IFLHS->getInstallName()), 324 DiffScalarVal<StringRef, AD_Diff_Scalar_Str>(rhs, 325 IFRHS->getInstallName()), 326 "Install Name")); 327 328 if (IFLHS->getCurrentVersion() != IFRHS->getCurrentVersion()) 329 Output.push_back(recordDifferences( 330 DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>( 331 lhs, IFLHS->getCurrentVersion()), 332 DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>( 333 rhs, IFRHS->getCurrentVersion()), 334 "Current Version")); 335 if (IFLHS->getCompatibilityVersion() != IFRHS->getCompatibilityVersion()) 336 Output.push_back(recordDifferences( 337 DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>( 338 lhs, IFLHS->getCompatibilityVersion()), 339 DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>( 340 rhs, IFRHS->getCompatibilityVersion()), 341 "Compatibility Version")); 342 if (IFLHS->getSwiftABIVersion() != IFRHS->getSwiftABIVersion()) 343 Output.push_back( 344 recordDifferences(DiffScalarVal<uint8_t, AD_Diff_Scalar_Unsigned>( 345 lhs, IFLHS->getSwiftABIVersion()), 346 DiffScalarVal<uint8_t, AD_Diff_Scalar_Unsigned>( 347 rhs, IFRHS->getSwiftABIVersion()), 348 "Swift ABI Version")); 349 350 if (IFLHS->isTwoLevelNamespace() != IFRHS->isTwoLevelNamespace()) 351 Output.push_back(recordDifferences(DiffScalarVal<bool, AD_Diff_Scalar_Bool>( 352 lhs, IFLHS->isTwoLevelNamespace()), 353 DiffScalarVal<bool, AD_Diff_Scalar_Bool>( 354 rhs, IFRHS->isTwoLevelNamespace()), 355 "Two Level Namespace")); 356 357 if (IFLHS->isApplicationExtensionSafe() != 358 IFRHS->isApplicationExtensionSafe()) 359 Output.push_back( 360 recordDifferences(DiffScalarVal<bool, AD_Diff_Scalar_Bool>( 361 lhs, IFLHS->isApplicationExtensionSafe()), 362 DiffScalarVal<bool, AD_Diff_Scalar_Bool>( 363 rhs, IFRHS->isApplicationExtensionSafe()), 364 "Application Extension Safe")); 365 366 if (IFLHS->hasSimulatorSupport() != IFRHS->hasSimulatorSupport()) 367 Output.push_back(recordDifferences(DiffScalarVal<bool, AD_Diff_Scalar_Bool>( 368 lhs, IFLHS->hasSimulatorSupport()), 369 DiffScalarVal<bool, AD_Diff_Scalar_Bool>( 370 rhs, IFRHS->hasSimulatorSupport()), 371 "Simulator Support")); 372 373 if (IFLHS->reexportedLibraries() != IFRHS->reexportedLibraries()) 374 Output.push_back(recordDifferences(IFLHS->reexportedLibraries(), 375 IFRHS->reexportedLibraries(), 376 "Reexported Libraries")); 377 378 if (IFLHS->allowableClients() != IFRHS->allowableClients()) 379 Output.push_back(recordDifferences(IFLHS->allowableClients(), 380 IFRHS->allowableClients(), 381 "Allowable Clients")); 382 383 if (IFLHS->umbrellas() != IFRHS->umbrellas()) 384 Output.push_back(recordDifferences(IFLHS->umbrellas(), IFRHS->umbrellas(), 385 "Parent Umbrellas")); 386 387 if (!checkSymbolEquality(IFLHS->symbols(), IFRHS->symbols())) 388 Output.push_back( 389 recordDifferences(IFLHS->symbols(), IFRHS->symbols(), "Symbols")); 390 391 if (IFLHS->documents() != IFRHS->documents()) { 392 DiffOutput Docs("Inlined Reexported Frameworks/Libraries"); 393 Docs.Kind = AD_Inline_Doc; 394 std::vector<StringRef> DocsInserted; 395 // Iterate through inline frameworks/libraries from interface file and find 396 // match based on install name. 397 for (auto DocLHS : IFLHS->documents()) { 398 auto Pair = llvm::find_if(IFRHS->documents(), [&](const auto &DocRHS) { 399 return (DocLHS->getInstallName() == DocRHS->getInstallName()); 400 }); 401 // If a match found, recursively get differences between the pair. 402 if (Pair != IFRHS->documents().end()) { 403 InlineDoc PairDiff = 404 InlineDoc(DocLHS->getInstallName(), 405 findDifferences(DocLHS.get(), Pair->get())); 406 if (!PairDiff.DocValues.empty()) 407 Docs.Values.push_back( 408 std::make_unique<InlineDoc>(std::move(PairDiff))); 409 } 410 // If a match is not found, get attributes from single item. 411 else 412 Docs.Values.push_back(std::make_unique<InlineDoc>(InlineDoc( 413 DocLHS->getInstallName(), getSingleIF(DocLHS.get(), lhs)))); 414 DocsInserted.push_back(DocLHS->getInstallName()); 415 } 416 for (auto DocRHS : IFRHS->documents()) { 417 auto WasGathered = 418 llvm::any_of(DocsInserted, [&](const auto &GatheredDoc) { 419 return (GatheredDoc == DocRHS->getInstallName()); 420 }); 421 if (!WasGathered) 422 Docs.Values.push_back(std::make_unique<InlineDoc>(InlineDoc( 423 DocRHS->getInstallName(), getSingleIF(DocRHS.get(), rhs)))); 424 } 425 if (!Docs.Values.empty()) 426 Output.push_back(std::move(Docs)); 427 } 428 return Output; 429 } 430 431 template <typename T> 432 void printSingleVal(std::string Indent, const DiffOutput &Attr, 433 raw_ostream &OS) { 434 if (Attr.Values.empty()) 435 return; 436 OS << Indent << Attr.Name << "\n"; 437 for (auto &RawItem : Attr.Values) 438 if (T *Item = dyn_cast<T>(RawItem.get())) 439 Item->print(OS, Indent); 440 } 441 442 template <typename T> 443 T *castValues(const std::unique_ptr<AttributeDiff> &RawAttr) { 444 T *CastAttr = cast<T>(RawAttr.get()); 445 return CastAttr; 446 } 447 448 template <typename T> void sortTargetValues(std::vector<T> &TargValues) { 449 llvm::stable_sort(TargValues, [](const auto &ValA, const auto &ValB) { 450 if (ValA.getOrder() == ValB.getOrder()) { 451 return ValA.getVal() < ValB.getVal(); 452 } 453 return ValA.getOrder() < ValB.getOrder(); 454 }); 455 } 456 457 template <typename T> 458 void printVecVal(std::string Indent, const DiffOutput &Attr, raw_ostream &OS) { 459 if (Attr.Values.empty()) 460 return; 461 462 OS << Indent << Attr.Name << "\n"; 463 464 std::vector<T *> SortedAttrs; 465 466 llvm::transform(Attr.Values, std::back_inserter(SortedAttrs), castValues<T>); 467 468 llvm::sort(SortedAttrs, [&](const auto &ValA, const auto &ValB) { 469 return ValA->Targ < ValB->Targ; 470 }); 471 472 for (auto *Vec : SortedAttrs) { 473 sortTargetValues<DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>( 474 Vec->TargValues); 475 OS << Indent << "\t" << getTargetTripleName(Vec->Targ) << "\n"; 476 for (auto &Item : Vec->TargValues) 477 Item.print(OS, Indent); 478 } 479 } 480 481 template <> 482 void printVecVal<DiffSymVec>(std::string Indent, const DiffOutput &Attr, 483 raw_ostream &OS) { 484 if (Attr.Values.empty()) 485 return; 486 487 OS << Indent << Attr.Name << "\n"; 488 489 std::vector<DiffSymVec *> SortedAttrs; 490 491 llvm::transform(Attr.Values, std::back_inserter(SortedAttrs), 492 castValues<DiffSymVec>); 493 494 llvm::sort(SortedAttrs, [&](const auto &ValA, const auto &ValB) { 495 return ValA->Targ < ValB->Targ; 496 }); 497 for (auto *SymVec : SortedAttrs) { 498 sortTargetValues<SymScalar>(SymVec->TargValues); 499 OS << Indent << "\t" << getTargetTripleName(SymVec->Targ) << "\n"; 500 for (auto &Item : SymVec->TargValues) 501 Item.print(OS, Indent, SymVec->Targ); 502 } 503 } 504 505 void DiffEngine::printDifferences(raw_ostream &OS, 506 const std::vector<DiffOutput> &Diffs, 507 int IndentCounter) { 508 std::string Indent = std::string(IndentCounter, '\t'); 509 for (auto &Attr : Diffs) { 510 switch (Attr.Kind) { 511 case AD_Diff_Scalar_Str: 512 if (IndentCounter == 0) 513 printSingleVal<DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>(Indent, 514 Attr, OS); 515 break; 516 case AD_Diff_Scalar_PackedVersion: 517 printSingleVal< 518 DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>>(Indent, 519 Attr, OS); 520 break; 521 case AD_Diff_Scalar_Unsigned: 522 printSingleVal<DiffScalarVal<uint8_t, AD_Diff_Scalar_Unsigned>>(Indent, 523 Attr, OS); 524 break; 525 case AD_Diff_Scalar_Bool: 526 printSingleVal<DiffScalarVal<bool, AD_Diff_Scalar_Bool>>(Indent, Attr, 527 OS); 528 break; 529 case AD_Str_Vec: 530 printVecVal<DiffStrVec>(Indent, Attr, OS); 531 break; 532 case AD_Sym_Vec: 533 printVecVal<DiffSymVec>(Indent, Attr, OS); 534 break; 535 case AD_Inline_Doc: 536 if (!Attr.Values.empty()) { 537 OS << Indent << Attr.Name << "\n"; 538 for (auto &Item : Attr.Values) 539 if (InlineDoc *Doc = dyn_cast<InlineDoc>(Item.get())) 540 if (!Doc->DocValues.empty()) { 541 OS << Indent << "\t" << Doc->InstallName << "\n"; 542 printDifferences(OS, std::move(Doc->DocValues), 2); 543 } 544 } 545 break; 546 } 547 } 548 } 549 550 bool DiffEngine::compareFiles(raw_ostream &OS) { 551 const auto *IFLHS = &(FileLHS->getInterfaceFile()); 552 const auto *IFRHS = &(FileRHS->getInterfaceFile()); 553 if (*IFLHS == *IFRHS) 554 return false; 555 OS << "< " << std::string(IFLHS->getPath().data()) << "\n> " 556 << std::string(IFRHS->getPath().data()) << "\n\n"; 557 std::vector<DiffOutput> Diffs = findDifferences(IFLHS, IFRHS); 558 printDifferences(OS, Diffs, 0); 559 return true; 560 } 561