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
22using namespace llvm;
23using namespace MachO;
24using namespace object;
25
26StringRef 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.
33namespace llvm {
34
35template <typename T, DiffAttrKind U>
36inline void DiffScalarVal<T, U>::print(raw_ostream &OS, std::string Indent) {
37 OS << Indent << "\t" << setOrderIndicator(Order) << Val << "\n";
38}
39
40template <>
41inline void
42DiffScalarVal<StringRef, AD_Diff_Scalar_Str>::print(raw_ostream &OS,
43 std::string Indent) {
44 OS << Indent << "\t\t" << setOrderIndicator(Order) << Val << "\n";
45}
46
47template <>
48inline void
49DiffScalarVal<uint8_t, AD_Diff_Scalar_Unsigned>::print(raw_ostream &OS,
50 std::string Indent) {
51 OS << Indent << "\t" << setOrderIndicator(Order) << std::to_string(val: Val)
52 << "\n";
53}
54
55template <>
56inline void
57DiffScalarVal<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
65StringLiteral SymScalar::getSymbolNamePrefix(MachO::EncodeKind Kind) {
66 switch (Kind) {
67 case MachO::EncodeKind::GlobalSymbol:
68 return StringLiteral("");
69 case MachO::EncodeKind::ObjectiveCClass:
70 return ObjC2MetaClassNamePrefix;
71 case MachO::EncodeKind ::ObjectiveCClassEHType:
72 return ObjC2EHTypePrefix;
73 case MachO::EncodeKind ::ObjectiveCInstanceVariable:
74 return ObjC2IVarPrefix;
75 }
76 llvm_unreachable("Unknown llvm::MachO::EncodeKind enum");
77}
78
79std::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(RHS: "Thread-Local ");
85 if (Sym->isWeakDefined())
86 Flags.append(RHS: "Weak-Defined ");
87 if (Sym->isWeakReferenced())
88 Flags.append(RHS: "Weak-Referenced ");
89 if (Sym->isUndefined())
90 Flags.append(RHS: "Undefined ");
91 if (Sym->isReexported())
92 Flags.append(RHS: "Reexported ");
93 if (Sym->isData())
94 Flags.append(RHS: "Data ");
95 if (Sym->isText())
96 Flags.append(RHS: "Text ");
97
98 return std::string(Flags);
99}
100
101void SymScalar::print(raw_ostream &OS, std::string Indent, MachO::Target Targ) {
102 if (Val->getKind() == MachO::EncodeKind::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(Sym: Val)
106 << "\n";
107 return;
108 }
109 OS << Indent << "\t\t" << ((Order == lhs) ? "< " : "> ")
110 << ObjC2ClassNamePrefix << Val->getName() << getFlagString(Sym: Val) << "\n";
111 }
112 OS << Indent << "\t\t" << ((Order == lhs) ? "< " : "> ")
113 << getSymbolNamePrefix(Kind: Val->getKind()) << Val->getName()
114 << getFlagString(Sym: Val) << "\n";
115}
116
117bool checkSymbolEquality(llvm::MachO::InterfaceFile::const_symbol_range LHS,
118 llvm::MachO::InterfaceFile::const_symbol_range RHS) {
119 if (std::distance(first: LHS.begin(), last: LHS.end()) !=
120 std::distance(first: RHS.begin(), last: RHS.end()))
121 return false;
122 return std::equal(first1: LHS.begin(), last1: LHS.end(), first2: RHS.begin(),
123 binary_pred: [&](auto LHS, auto RHS) { return *LHS == *RHS; });
124}
125
126template <typename TargetVecT, typename ValTypeT, typename V>
127void 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
146DiffOutput 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 Val: IRef.getInstallName(), Targ, Diff, Order);
155 return Diff;
156}
157
158DiffOutput
159getSingleAttrDiff(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 Val: StringRef(Pair.second), Targ: Pair.first, Diff, Order);
167 return Diff;
168}
169
170DiffOutput 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>(Val: Sym, Targ, Diff, Order);
177 return Diff;
178}
179
180template <typename T>
181DiffOutput 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
188template <typename T, DiffAttrKind U>
189void diffAttribute(std::string Name, std::vector<DiffOutput> &Output,
190 DiffScalarVal<T, U> Attr) {
191 Output.push_back(getSingleAttrDiff(Attr, Name));
192}
193
194template <typename T>
195void diffAttribute(std::string Name, std::vector<DiffOutput> &Output,
196 const T &Val, InterfaceInputOrder Order) {
197 Output.push_back(getSingleAttrDiff(Val, Name, Order));
198}
199
200std::vector<DiffOutput> getSingleIF(InterfaceFile *Interface,
201 InterfaceInputOrder Order) {
202 std::vector<DiffOutput> Output;
203 diffAttribute(Name: "Install Name", Output,
204 Attr: DiffScalarVal<StringRef, AD_Diff_Scalar_Str>(
205 Order, Interface->getInstallName()));
206 for (const auto &Doc : Interface->documents()) {
207 DiffOutput Documents("Inlined Reexported Frameworks/Libraries");
208 Documents.Kind = AD_Inline_Doc;
209 Documents.Values.push_back(x: std::make_unique<InlineDoc>(
210 args: InlineDoc(Doc->getInstallName(), getSingleIF(Interface: Doc.get(), Order))));
211 Output.push_back(x: std::move(Documents));
212 }
213 return Output;
214}
215
216void findAndAddDiff(const std::vector<InterfaceFileRef> &CollectedIRefVec,
217 const std::vector<InterfaceFileRef> &LookupIRefVec,
218 DiffOutput &Result, InterfaceInputOrder Order) {
219 Result.Kind = AD_Str_Vec;
220 for (const auto &IRef : CollectedIRefVec)
221 for (auto Targ : IRef.targets()) {
222 auto FoundIRef = llvm::any_of(Range: LookupIRefVec, P: [&](const auto LIRef) {
223 return llvm::is_contained(LIRef.targets(), Targ) &&
224 IRef.getInstallName() == LIRef.getInstallName();
225 });
226 if (!FoundIRef)
227 addDiffForTargSlice<DiffStrVec,
228 DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>(
229 Val: IRef.getInstallName(), Targ, Diff&: Result, Order);
230 }
231}
232
233void findAndAddDiff(
234 const std::vector<std::pair<Target, std::string>> &CollectedPairs,
235 const std::vector<std::pair<Target, std::string>> &LookupPairs,
236 DiffOutput &Result, InterfaceInputOrder Order) {
237 Result.Kind = AD_Str_Vec;
238 for (const auto &Pair : CollectedPairs) {
239 auto FoundPair = llvm::find(Range: LookupPairs, Val: Pair);
240 if (FoundPair == LookupPairs.end())
241 addDiffForTargSlice<DiffStrVec,
242 DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>(
243 Val: StringRef(Pair.second), Targ: Pair.first, Diff&: Result, Order);
244 }
245}
246
247void findAndAddDiff(InterfaceFile::const_symbol_range CollectedSyms,
248 InterfaceFile::const_symbol_range LookupSyms,
249 DiffOutput &Result, InterfaceInputOrder Order) {
250 Result.Kind = AD_Sym_Vec;
251 for (const auto *Sym : CollectedSyms)
252 for (const auto Targ : Sym->targets()) {
253 auto FoundSym = llvm::any_of(Range&: LookupSyms, P: [&](const auto LSym) {
254 return (Sym->getName() == LSym->getName() &&
255 Sym->getKind() == LSym->getKind() &&
256 Sym->getFlags() == LSym->getFlags() &&
257 llvm::is_contained(LSym->targets(), Targ));
258 });
259 if (!FoundSym)
260 addDiffForTargSlice<DiffSymVec, SymScalar>(Val: Sym, Targ, Diff&: Result, Order);
261 }
262}
263
264template <typename T>
265DiffOutput recordDifferences(T LHS, T RHS, std::string Attr) {
266 DiffOutput Diff(Attr);
267 if (LHS.getKind() == RHS.getKind()) {
268 Diff.Kind = LHS.getKind();
269 Diff.Values.push_back(std::make_unique<T>(LHS));
270 Diff.Values.push_back(std::make_unique<T>(RHS));
271 }
272 return Diff;
273}
274
275template <typename T>
276DiffOutput recordDifferences(const std::vector<T> &LHS,
277 const std::vector<T> &RHS, std::string Attr) {
278 DiffOutput Diff(Attr);
279 Diff.Kind = AD_Str_Vec;
280 findAndAddDiff(LHS, RHS, Diff, lhs);
281 findAndAddDiff(RHS, LHS, Diff, rhs);
282 return Diff;
283}
284
285DiffOutput recordDifferences(llvm::MachO::InterfaceFile::const_symbol_range LHS,
286 llvm::MachO::InterfaceFile::const_symbol_range RHS,
287 std::string Attr) {
288 DiffOutput Diff(Attr);
289 Diff.Kind = AD_Sym_Vec;
290 findAndAddDiff(CollectedSyms: LHS, LookupSyms: RHS, Result&: Diff, Order: lhs);
291 findAndAddDiff(CollectedSyms: RHS, LookupSyms: LHS, Result&: Diff, Order: rhs);
292 return Diff;
293}
294
295std::vector<DiffOutput>
296DiffEngine::findDifferences(const InterfaceFile *IFLHS,
297 const InterfaceFile *IFRHS) {
298 std::vector<DiffOutput> Output;
299 if (IFLHS->getInstallName() != IFRHS->getInstallName())
300 Output.push_back(x: recordDifferences(
301 LHS: DiffScalarVal<StringRef, AD_Diff_Scalar_Str>(lhs,
302 IFLHS->getInstallName()),
303 RHS: DiffScalarVal<StringRef, AD_Diff_Scalar_Str>(rhs,
304 IFRHS->getInstallName()),
305 Attr: "Install Name"));
306
307 if (IFLHS->getCurrentVersion() != IFRHS->getCurrentVersion())
308 Output.push_back(x: recordDifferences(
309 LHS: DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>(
310 lhs, IFLHS->getCurrentVersion()),
311 RHS: DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>(
312 rhs, IFRHS->getCurrentVersion()),
313 Attr: "Current Version"));
314 if (IFLHS->getCompatibilityVersion() != IFRHS->getCompatibilityVersion())
315 Output.push_back(x: recordDifferences(
316 LHS: DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>(
317 lhs, IFLHS->getCompatibilityVersion()),
318 RHS: DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>(
319 rhs, IFRHS->getCompatibilityVersion()),
320 Attr: "Compatibility Version"));
321 if (IFLHS->getSwiftABIVersion() != IFRHS->getSwiftABIVersion())
322 Output.push_back(
323 x: recordDifferences(LHS: DiffScalarVal<uint8_t, AD_Diff_Scalar_Unsigned>(
324 lhs, IFLHS->getSwiftABIVersion()),
325 RHS: DiffScalarVal<uint8_t, AD_Diff_Scalar_Unsigned>(
326 rhs, IFRHS->getSwiftABIVersion()),
327 Attr: "Swift ABI Version"));
328
329 if (IFLHS->isTwoLevelNamespace() != IFRHS->isTwoLevelNamespace())
330 Output.push_back(x: recordDifferences(LHS: DiffScalarVal<bool, AD_Diff_Scalar_Bool>(
331 lhs, IFLHS->isTwoLevelNamespace()),
332 RHS: DiffScalarVal<bool, AD_Diff_Scalar_Bool>(
333 rhs, IFRHS->isTwoLevelNamespace()),
334 Attr: "Two Level Namespace"));
335
336 if (IFLHS->isApplicationExtensionSafe() !=
337 IFRHS->isApplicationExtensionSafe())
338 Output.push_back(
339 x: recordDifferences(LHS: DiffScalarVal<bool, AD_Diff_Scalar_Bool>(
340 lhs, IFLHS->isApplicationExtensionSafe()),
341 RHS: DiffScalarVal<bool, AD_Diff_Scalar_Bool>(
342 rhs, IFRHS->isApplicationExtensionSafe()),
343 Attr: "Application Extension Safe"));
344
345 if (IFLHS->hasSimulatorSupport() != IFRHS->hasSimulatorSupport())
346 Output.push_back(x: recordDifferences(LHS: DiffScalarVal<bool, AD_Diff_Scalar_Bool>(
347 lhs, IFLHS->hasSimulatorSupport()),
348 RHS: DiffScalarVal<bool, AD_Diff_Scalar_Bool>(
349 rhs, IFRHS->hasSimulatorSupport()),
350 Attr: "Simulator Support"));
351
352 if (IFLHS->isOSLibNotForSharedCache() != IFRHS->isOSLibNotForSharedCache())
353 Output.push_back(
354 x: recordDifferences(LHS: DiffScalarVal<bool, AD_Diff_Scalar_Bool>(
355 lhs, IFLHS->isOSLibNotForSharedCache()),
356 RHS: DiffScalarVal<bool, AD_Diff_Scalar_Bool>(
357 rhs, IFRHS->isOSLibNotForSharedCache()),
358 Attr: "Shared Cache Ineligible"));
359
360 if (IFLHS->reexportedLibraries() != IFRHS->reexportedLibraries())
361 Output.push_back(x: recordDifferences(LHS: IFLHS->reexportedLibraries(),
362 RHS: IFRHS->reexportedLibraries(),
363 Attr: "Reexported Libraries"));
364
365 if (IFLHS->rpaths() != IFRHS->rpaths())
366 Output.push_back(x: recordDifferences(LHS: IFLHS->rpaths(), RHS: IFRHS->rpaths(),
367 Attr: "Run Path Search Paths"));
368
369 if (IFLHS->allowableClients() != IFRHS->allowableClients())
370 Output.push_back(x: recordDifferences(LHS: IFLHS->allowableClients(),
371 RHS: IFRHS->allowableClients(),
372 Attr: "Allowable Clients"));
373
374 if (IFLHS->umbrellas() != IFRHS->umbrellas())
375 Output.push_back(x: recordDifferences(LHS: IFLHS->umbrellas(), RHS: IFRHS->umbrellas(),
376 Attr: "Parent Umbrellas"));
377
378 if (!checkSymbolEquality(LHS: IFLHS->symbols(), RHS: IFRHS->symbols()))
379 Output.push_back(
380 x: recordDifferences(LHS: IFLHS->symbols(), RHS: IFRHS->symbols(), Attr: "Symbols"));
381
382 if (IFLHS->documents() != IFRHS->documents()) {
383 DiffOutput Docs("Inlined Reexported Frameworks/Libraries");
384 Docs.Kind = AD_Inline_Doc;
385 std::vector<StringRef> DocsInserted;
386 // Iterate through inline frameworks/libraries from interface file and find
387 // match based on install name.
388 for (auto DocLHS : IFLHS->documents()) {
389 auto Pair = llvm::find_if(Range: IFRHS->documents(), P: [&](const auto &DocRHS) {
390 return (DocLHS->getInstallName() == DocRHS->getInstallName());
391 });
392 // If a match found, recursively get differences between the pair.
393 if (Pair != IFRHS->documents().end()) {
394 InlineDoc PairDiff =
395 InlineDoc(DocLHS->getInstallName(),
396 findDifferences(IFLHS: DocLHS.get(), IFRHS: Pair->get()));
397 if (!PairDiff.DocValues.empty())
398 Docs.Values.push_back(
399 x: std::make_unique<InlineDoc>(args: std::move(PairDiff)));
400 }
401 // No matching inlined library was found.
402 else
403 Docs.Values.push_back(x: std::make_unique<InlineDoc>(
404 args: InlineDoc(DocLHS->getInstallName(), getSingleIF(Interface: DocLHS.get(), Order: lhs),
405 /*IsMissingDoc=*/true)));
406 DocsInserted.push_back(x: DocLHS->getInstallName());
407 }
408 for (auto DocRHS : IFRHS->documents()) {
409 auto WasGathered =
410 llvm::any_of(Range&: DocsInserted, P: [&](const auto &GatheredDoc) {
411 return (GatheredDoc == DocRHS->getInstallName());
412 });
413 if (!WasGathered)
414 Docs.Values.push_back(x: std::make_unique<InlineDoc>(
415 args: InlineDoc(DocRHS->getInstallName(), getSingleIF(Interface: DocRHS.get(), Order: rhs),
416 /*IsMissingDoc=*/true)));
417 }
418 if (!Docs.Values.empty())
419 Output.push_back(x: std::move(Docs));
420 }
421 return Output;
422}
423
424template <typename T>
425void printSingleVal(std::string Indent, const DiffOutput &Attr,
426 raw_ostream &OS) {
427 if (Attr.Values.empty())
428 return;
429 OS << Indent << Attr.Name << "\n";
430 for (auto &RawItem : Attr.Values)
431 if (T *Item = dyn_cast<T>(RawItem.get()))
432 Item->print(OS, Indent);
433}
434
435template <typename T>
436T *castValues(const std::unique_ptr<AttributeDiff> &RawAttr) {
437 T *CastAttr = cast<T>(RawAttr.get());
438 return CastAttr;
439}
440
441template <typename T> void sortTargetValues(std::vector<T> &TargValues) {
442 llvm::stable_sort(TargValues, [](const auto &ValA, const auto &ValB) {
443 if (ValA.getOrder() == ValB.getOrder()) {
444 return ValA.getVal() < ValB.getVal();
445 }
446 return ValA.getOrder() < ValB.getOrder();
447 });
448}
449
450template <typename T>
451void printVecVal(std::string Indent, const DiffOutput &Attr, raw_ostream &OS) {
452 if (Attr.Values.empty()) {
453 OS << Indent << "'" << Attr.Name << "' differ by order\n";
454 return;
455 }
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
476template <>
477void 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(Range: Attr.Values, d_first: std::back_inserter(x&: SortedAttrs),
487 F: castValues<DiffSymVec>);
488
489 llvm::sort(C&: SortedAttrs, Comp: [&](const auto &ValA, const auto &ValB) {
490 return ValA->Targ < ValB->Targ;
491 });
492 for (auto *SymVec : SortedAttrs) {
493 sortTargetValues<SymScalar>(TargValues&: SymVec->TargValues);
494 OS << Indent << "\t" << getTargetTripleName(Targ: SymVec->Targ) << "\n";
495 for (auto &Item : SymVec->TargValues)
496 Item.print(OS, Indent, Targ: SymVec->Targ);
497 }
498}
499
500void 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>(Val: Item.get())) {
535 if (Doc->DocValues.empty())
536 continue;
537 IndentCounter = 2;
538 // When only one input file contains an inlined library, print out
539 // the install name for it. Otherwise print out the different values
540 // by the install name.
541 if (Doc->IsMissingDoc) {
542 printSingleVal<DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>(
543 Indent: std::string(IndentCounter, '\t'), Attr: Doc->DocValues.front(), OS);
544 } else {
545 OS << Indent << "\t" << Doc->InstallName << "\n";
546 printDifferences(OS, Diffs: std::move(Doc->DocValues), IndentCounter);
547 }
548 }
549 }
550 }
551 break;
552 }
553 }
554}
555
556bool DiffEngine::compareFiles(raw_ostream &OS) {
557 if (*FileLHS == *FileRHS)
558 return false;
559 OS << "< " << std::string(FileLHS->getPath().data()) << "\n> "
560 << std::string(FileRHS->getPath().data()) << "\n\n";
561 std::vector<DiffOutput> Diffs = findDifferences(IFLHS: FileLHS, IFRHS: FileRHS);
562 printDifferences(OS, Diffs, IndentCounter: 0);
563 return true;
564}
565