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