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