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