Make test more lenient for custom clang version strings
[llvm-project.git] / llvm / tools / llvm-readtapi / DiffEngine.cpp
blob6434c871fa64b9885f858eab4406466d1c76f5de
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.
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>
22 using namespace llvm;
23 using namespace MachO;
24 using namespace object;
26 StringRef setOrderIndicator(InterfaceInputOrder Order) {
27 return ((Order == lhs) ? "< " : "> ");
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 {
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";
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";
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";
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";
63 } // end namespace llvm
65 StringLiteral 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;
76 llvm_unreachable("Unknown llvm::MachO::EncodeKind enum");
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 ");
98 return std::string(Flags);
101 void 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(Val)
106 << "\n";
107 return;
109 OS << Indent << "\t\t" << ((Order == lhs) ? "< " : "> ")
110 << ObjC2ClassNamePrefix << Val->getName() << getFlagString(Val) << "\n";
112 OS << Indent << "\t\t" << ((Order == lhs) ? "< " : "> ")
113 << getSymbolNamePrefix(Val->getKind()) << Val->getName()
114 << getFlagString(Val) << "\n";
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; });
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;
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));
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;
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;
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;
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;
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));
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));
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 (const 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));
234 return Output;
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();
247 if (!FoundIRef)
248 addDiffForTargSlice<DiffStrVec,
249 DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>(
250 IRef.getInstallName(), Targ, Result, Order);
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);
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));
280 if (!FoundSym)
281 addDiffForTargSlice<DiffSymVec, SymScalar>(Sym, Targ, Result, Order);
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));
293 return Diff;
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;
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;
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"));
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"));
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"));
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"));
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"));
373 if (IFLHS->isOSLibNotForSharedCache() != IFRHS->isOSLibNotForSharedCache())
374 Output.push_back(
375 recordDifferences(DiffScalarVal<bool, AD_Diff_Scalar_Bool>(
376 lhs, IFLHS->isOSLibNotForSharedCache()),
377 DiffScalarVal<bool, AD_Diff_Scalar_Bool>(
378 rhs, IFRHS->isOSLibNotForSharedCache()),
379 "Shared Cache Ineligible"));
381 if (IFLHS->reexportedLibraries() != IFRHS->reexportedLibraries())
382 Output.push_back(recordDifferences(IFLHS->reexportedLibraries(),
383 IFRHS->reexportedLibraries(),
384 "Reexported Libraries"));
386 if (IFLHS->rpaths() != IFRHS->rpaths())
387 Output.push_back(recordDifferences(IFLHS->rpaths(), IFRHS->rpaths(),
388 "Run Path Search Paths"));
390 if (IFLHS->allowableClients() != IFRHS->allowableClients())
391 Output.push_back(recordDifferences(IFLHS->allowableClients(),
392 IFRHS->allowableClients(),
393 "Allowable Clients"));
395 if (IFLHS->umbrellas() != IFRHS->umbrellas())
396 Output.push_back(recordDifferences(IFLHS->umbrellas(), IFRHS->umbrellas(),
397 "Parent Umbrellas"));
399 if (!checkSymbolEquality(IFLHS->symbols(), IFRHS->symbols()))
400 Output.push_back(
401 recordDifferences(IFLHS->symbols(), IFRHS->symbols(), "Symbols"));
403 if (IFLHS->documents() != IFRHS->documents()) {
404 DiffOutput Docs("Inlined Reexported Frameworks/Libraries");
405 Docs.Kind = AD_Inline_Doc;
406 std::vector<StringRef> DocsInserted;
407 // Iterate through inline frameworks/libraries from interface file and find
408 // match based on install name.
409 for (auto DocLHS : IFLHS->documents()) {
410 auto Pair = llvm::find_if(IFRHS->documents(), [&](const auto &DocRHS) {
411 return (DocLHS->getInstallName() == DocRHS->getInstallName());
413 // If a match found, recursively get differences between the pair.
414 if (Pair != IFRHS->documents().end()) {
415 InlineDoc PairDiff =
416 InlineDoc(DocLHS->getInstallName(),
417 findDifferences(DocLHS.get(), Pair->get()));
418 if (!PairDiff.DocValues.empty())
419 Docs.Values.push_back(
420 std::make_unique<InlineDoc>(std::move(PairDiff)));
422 // If a match is not found, get attributes from single item.
423 else
424 Docs.Values.push_back(std::make_unique<InlineDoc>(InlineDoc(
425 DocLHS->getInstallName(), getSingleIF(DocLHS.get(), lhs))));
426 DocsInserted.push_back(DocLHS->getInstallName());
428 for (auto DocRHS : IFRHS->documents()) {
429 auto WasGathered =
430 llvm::any_of(DocsInserted, [&](const auto &GatheredDoc) {
431 return (GatheredDoc == DocRHS->getInstallName());
433 if (!WasGathered)
434 Docs.Values.push_back(std::make_unique<InlineDoc>(InlineDoc(
435 DocRHS->getInstallName(), getSingleIF(DocRHS.get(), rhs))));
437 if (!Docs.Values.empty())
438 Output.push_back(std::move(Docs));
440 return Output;
443 template <typename T>
444 void printSingleVal(std::string Indent, const DiffOutput &Attr,
445 raw_ostream &OS) {
446 if (Attr.Values.empty())
447 return;
448 OS << Indent << Attr.Name << "\n";
449 for (auto &RawItem : Attr.Values)
450 if (T *Item = dyn_cast<T>(RawItem.get()))
451 Item->print(OS, Indent);
454 template <typename T>
455 T *castValues(const std::unique_ptr<AttributeDiff> &RawAttr) {
456 T *CastAttr = cast<T>(RawAttr.get());
457 return CastAttr;
460 template <typename T> void sortTargetValues(std::vector<T> &TargValues) {
461 llvm::stable_sort(TargValues, [](const auto &ValA, const auto &ValB) {
462 if (ValA.getOrder() == ValB.getOrder()) {
463 return ValA.getVal() < ValB.getVal();
465 return ValA.getOrder() < ValB.getOrder();
469 template <typename T>
470 void printVecVal(std::string Indent, const DiffOutput &Attr, raw_ostream &OS) {
471 if (Attr.Values.empty())
472 return;
474 OS << Indent << Attr.Name << "\n";
476 std::vector<T *> SortedAttrs;
478 llvm::transform(Attr.Values, std::back_inserter(SortedAttrs), castValues<T>);
480 llvm::sort(SortedAttrs, [&](const auto &ValA, const auto &ValB) {
481 return ValA->Targ < ValB->Targ;
484 for (auto *Vec : SortedAttrs) {
485 sortTargetValues<DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>(
486 Vec->TargValues);
487 OS << Indent << "\t" << getTargetTripleName(Vec->Targ) << "\n";
488 for (auto &Item : Vec->TargValues)
489 Item.print(OS, Indent);
493 template <>
494 void printVecVal<DiffSymVec>(std::string Indent, const DiffOutput &Attr,
495 raw_ostream &OS) {
496 if (Attr.Values.empty())
497 return;
499 OS << Indent << Attr.Name << "\n";
501 std::vector<DiffSymVec *> SortedAttrs;
503 llvm::transform(Attr.Values, std::back_inserter(SortedAttrs),
504 castValues<DiffSymVec>);
506 llvm::sort(SortedAttrs, [&](const auto &ValA, const auto &ValB) {
507 return ValA->Targ < ValB->Targ;
509 for (auto *SymVec : SortedAttrs) {
510 sortTargetValues<SymScalar>(SymVec->TargValues);
511 OS << Indent << "\t" << getTargetTripleName(SymVec->Targ) << "\n";
512 for (auto &Item : SymVec->TargValues)
513 Item.print(OS, Indent, SymVec->Targ);
517 void DiffEngine::printDifferences(raw_ostream &OS,
518 const std::vector<DiffOutput> &Diffs,
519 int IndentCounter) {
520 std::string Indent = std::string(IndentCounter, '\t');
521 for (auto &Attr : Diffs) {
522 switch (Attr.Kind) {
523 case AD_Diff_Scalar_Str:
524 if (IndentCounter == 0)
525 printSingleVal<DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>(Indent,
526 Attr, OS);
527 break;
528 case AD_Diff_Scalar_PackedVersion:
529 printSingleVal<
530 DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>>(Indent,
531 Attr, OS);
532 break;
533 case AD_Diff_Scalar_Unsigned:
534 printSingleVal<DiffScalarVal<uint8_t, AD_Diff_Scalar_Unsigned>>(Indent,
535 Attr, OS);
536 break;
537 case AD_Diff_Scalar_Bool:
538 printSingleVal<DiffScalarVal<bool, AD_Diff_Scalar_Bool>>(Indent, Attr,
539 OS);
540 break;
541 case AD_Str_Vec:
542 printVecVal<DiffStrVec>(Indent, Attr, OS);
543 break;
544 case AD_Sym_Vec:
545 printVecVal<DiffSymVec>(Indent, Attr, OS);
546 break;
547 case AD_Inline_Doc:
548 if (!Attr.Values.empty()) {
549 OS << Indent << Attr.Name << "\n";
550 for (auto &Item : Attr.Values)
551 if (InlineDoc *Doc = dyn_cast<InlineDoc>(Item.get()))
552 if (!Doc->DocValues.empty()) {
553 OS << Indent << "\t" << Doc->InstallName << "\n";
554 printDifferences(OS, std::move(Doc->DocValues), 2);
557 break;
562 bool DiffEngine::compareFiles(raw_ostream &OS) {
563 if (*FileLHS == *FileRHS)
564 return false;
565 OS << "< " << std::string(FileLHS->getPath().data()) << "\n> "
566 << std::string(FileRHS->getPath().data()) << "\n\n";
567 std::vector<DiffOutput> Diffs = findDifferences(FileLHS, FileRHS);
568 printDifferences(OS, Diffs, 0);
569 return true;