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