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