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