1 //===-- DiffEngine.cpp - Structural file comparison -----------------------===//
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
7 //===----------------------------------------------------------------------===//
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"
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.
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";
42 DiffScalarVal
<StringRef
, AD_Diff_Scalar_Str
>::print(raw_ostream
&OS
,
44 OS
<< Indent
<< "\t\t" << setOrderIndicator(Order
) << Val
<< "\n";
49 DiffScalarVal
<uint8_t, AD_Diff_Scalar_Unsigned
>::print(raw_ostream
&OS
,
51 OS
<< Indent
<< "\t" << setOrderIndicator(Order
) << std::to_string(Val
)
57 DiffScalarVal
<bool, AD_Diff_Scalar_Bool
>::print(raw_ostream
&OS
,
59 OS
<< Indent
<< "\t" << setOrderIndicator(Order
)
60 << ((Val
== true) ? "true" : "false") << "\n";
63 } // end namespace llvm
65 StringLiteral
SymScalar::getSymbolNamePrefix(MachO::SymbolKind Kind
) {
67 case MachO::SymbolKind::GlobalSymbol
:
68 return StringLiteral("");
69 case MachO::SymbolKind::ObjectiveCClass
:
70 return ObjC2MetaClassNamePrefix
;
71 case MachO::SymbolKind ::ObjectiveCClassEHType
:
72 return ObjC2EHTypePrefix
;
73 case MachO::SymbolKind ::ObjectiveCInstanceVariable
:
74 return ObjC2IVarPrefix
;
76 llvm_unreachable("Unknown llvm::MachO::SymbolKind enum");
79 std::string
SymScalar::getFlagString(const MachO::Symbol
*Sym
) {
80 if (Sym
->getFlags() == SymbolFlags::None
)
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 ");
94 Flags
.append("Data ");
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::SymbolKind::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
)
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()))
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
;
135 if (TargetVector
!= Diff
.Values
.end()) {
136 ValTypeT
NewVal(Order
, Val
);
137 cast
<TargetVecT
>(TargetVector
->get())->TargValues
.push_back(NewVal
);
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
);
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
);
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
);
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
));
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(),
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
));
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();
248 addDiffForTargSlice
<DiffStrVec
,
249 DiffScalarVal
<StringRef
, AD_Diff_Scalar_Str
>>(
250 IRef
.getInstallName(), Targ
, Result
, Order
);
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
));
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
));
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
);
306 DiffOutput
recordDifferences(llvm::MachO::InterfaceFile::const_symbol_range LHS
,
307 llvm::MachO::InterfaceFile::const_symbol_range RHS
,
309 DiffOutput
Diff(Attr
);
310 Diff
.Kind
= AD_Sym_Vec
;
311 findAndAddDiff(LHS
, RHS
, Diff
, lhs
);
312 findAndAddDiff(RHS
, LHS
, Diff
, rhs
);
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()),
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()),
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())
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())
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
->reexportedLibraries() != IFRHS
->reexportedLibraries())
374 Output
.push_back(recordDifferences(IFLHS
->reexportedLibraries(),
375 IFRHS
->reexportedLibraries(),
376 "Reexported Libraries"));
378 if (IFLHS
->rpaths() != IFRHS
->rpaths())
379 Output
.push_back(recordDifferences(IFLHS
->rpaths(), IFRHS
->rpaths(),
380 "Run Path Search Paths"));
382 if (IFLHS
->allowableClients() != IFRHS
->allowableClients())
383 Output
.push_back(recordDifferences(IFLHS
->allowableClients(),
384 IFRHS
->allowableClients(),
385 "Allowable Clients"));
387 if (IFLHS
->umbrellas() != IFRHS
->umbrellas())
388 Output
.push_back(recordDifferences(IFLHS
->umbrellas(), IFRHS
->umbrellas(),
389 "Parent Umbrellas"));
391 if (!checkSymbolEquality(IFLHS
->symbols(), IFRHS
->symbols()))
393 recordDifferences(IFLHS
->symbols(), IFRHS
->symbols(), "Symbols"));
395 if (IFLHS
->documents() != IFRHS
->documents()) {
396 DiffOutput
Docs("Inlined Reexported Frameworks/Libraries");
397 Docs
.Kind
= AD_Inline_Doc
;
398 std::vector
<StringRef
> DocsInserted
;
399 // Iterate through inline frameworks/libraries from interface file and find
400 // match based on install name.
401 for (auto DocLHS
: IFLHS
->documents()) {
402 auto Pair
= llvm::find_if(IFRHS
->documents(), [&](const auto &DocRHS
) {
403 return (DocLHS
->getInstallName() == DocRHS
->getInstallName());
405 // If a match found, recursively get differences between the pair.
406 if (Pair
!= IFRHS
->documents().end()) {
408 InlineDoc(DocLHS
->getInstallName(),
409 findDifferences(DocLHS
.get(), Pair
->get()));
410 if (!PairDiff
.DocValues
.empty())
411 Docs
.Values
.push_back(
412 std::make_unique
<InlineDoc
>(std::move(PairDiff
)));
414 // If a match is not found, get attributes from single item.
416 Docs
.Values
.push_back(std::make_unique
<InlineDoc
>(InlineDoc(
417 DocLHS
->getInstallName(), getSingleIF(DocLHS
.get(), lhs
))));
418 DocsInserted
.push_back(DocLHS
->getInstallName());
420 for (auto DocRHS
: IFRHS
->documents()) {
422 llvm::any_of(DocsInserted
, [&](const auto &GatheredDoc
) {
423 return (GatheredDoc
== DocRHS
->getInstallName());
426 Docs
.Values
.push_back(std::make_unique
<InlineDoc
>(InlineDoc(
427 DocRHS
->getInstallName(), getSingleIF(DocRHS
.get(), rhs
))));
429 if (!Docs
.Values
.empty())
430 Output
.push_back(std::move(Docs
));
435 template <typename T
>
436 void printSingleVal(std::string Indent
, const DiffOutput
&Attr
,
438 if (Attr
.Values
.empty())
440 OS
<< Indent
<< Attr
.Name
<< "\n";
441 for (auto &RawItem
: Attr
.Values
)
442 if (T
*Item
= dyn_cast
<T
>(RawItem
.get()))
443 Item
->print(OS
, Indent
);
446 template <typename T
>
447 T
*castValues(const std::unique_ptr
<AttributeDiff
> &RawAttr
) {
448 T
*CastAttr
= cast
<T
>(RawAttr
.get());
452 template <typename T
> void sortTargetValues(std::vector
<T
> &TargValues
) {
453 llvm::stable_sort(TargValues
, [](const auto &ValA
, const auto &ValB
) {
454 if (ValA
.getOrder() == ValB
.getOrder()) {
455 return ValA
.getVal() < ValB
.getVal();
457 return ValA
.getOrder() < ValB
.getOrder();
461 template <typename T
>
462 void printVecVal(std::string Indent
, const DiffOutput
&Attr
, raw_ostream
&OS
) {
463 if (Attr
.Values
.empty())
466 OS
<< Indent
<< Attr
.Name
<< "\n";
468 std::vector
<T
*> SortedAttrs
;
470 llvm::transform(Attr
.Values
, std::back_inserter(SortedAttrs
), castValues
<T
>);
472 llvm::sort(SortedAttrs
, [&](const auto &ValA
, const auto &ValB
) {
473 return ValA
->Targ
< ValB
->Targ
;
476 for (auto *Vec
: SortedAttrs
) {
477 sortTargetValues
<DiffScalarVal
<StringRef
, AD_Diff_Scalar_Str
>>(
479 OS
<< Indent
<< "\t" << getTargetTripleName(Vec
->Targ
) << "\n";
480 for (auto &Item
: Vec
->TargValues
)
481 Item
.print(OS
, Indent
);
486 void printVecVal
<DiffSymVec
>(std::string Indent
, const DiffOutput
&Attr
,
488 if (Attr
.Values
.empty())
491 OS
<< Indent
<< Attr
.Name
<< "\n";
493 std::vector
<DiffSymVec
*> SortedAttrs
;
495 llvm::transform(Attr
.Values
, std::back_inserter(SortedAttrs
),
496 castValues
<DiffSymVec
>);
498 llvm::sort(SortedAttrs
, [&](const auto &ValA
, const auto &ValB
) {
499 return ValA
->Targ
< ValB
->Targ
;
501 for (auto *SymVec
: SortedAttrs
) {
502 sortTargetValues
<SymScalar
>(SymVec
->TargValues
);
503 OS
<< Indent
<< "\t" << getTargetTripleName(SymVec
->Targ
) << "\n";
504 for (auto &Item
: SymVec
->TargValues
)
505 Item
.print(OS
, Indent
, SymVec
->Targ
);
509 void DiffEngine::printDifferences(raw_ostream
&OS
,
510 const std::vector
<DiffOutput
> &Diffs
,
512 std::string Indent
= std::string(IndentCounter
, '\t');
513 for (auto &Attr
: Diffs
) {
515 case AD_Diff_Scalar_Str
:
516 if (IndentCounter
== 0)
517 printSingleVal
<DiffScalarVal
<StringRef
, AD_Diff_Scalar_Str
>>(Indent
,
520 case AD_Diff_Scalar_PackedVersion
:
522 DiffScalarVal
<PackedVersion
, AD_Diff_Scalar_PackedVersion
>>(Indent
,
525 case AD_Diff_Scalar_Unsigned
:
526 printSingleVal
<DiffScalarVal
<uint8_t, AD_Diff_Scalar_Unsigned
>>(Indent
,
529 case AD_Diff_Scalar_Bool
:
530 printSingleVal
<DiffScalarVal
<bool, AD_Diff_Scalar_Bool
>>(Indent
, Attr
,
534 printVecVal
<DiffStrVec
>(Indent
, Attr
, OS
);
537 printVecVal
<DiffSymVec
>(Indent
, Attr
, OS
);
540 if (!Attr
.Values
.empty()) {
541 OS
<< Indent
<< Attr
.Name
<< "\n";
542 for (auto &Item
: Attr
.Values
)
543 if (InlineDoc
*Doc
= dyn_cast
<InlineDoc
>(Item
.get()))
544 if (!Doc
->DocValues
.empty()) {
545 OS
<< Indent
<< "\t" << Doc
->InstallName
<< "\n";
546 printDifferences(OS
, std::move(Doc
->DocValues
), 2);
554 bool DiffEngine::compareFiles(raw_ostream
&OS
) {
555 const auto *IFLHS
= &(FileLHS
->getInterfaceFile());
556 const auto *IFRHS
= &(FileRHS
->getInterfaceFile());
557 if (*IFLHS
== *IFRHS
)
559 OS
<< "< " << std::string(IFLHS
->getPath().data()) << "\n> "
560 << std::string(IFRHS
->getPath().data()) << "\n\n";
561 std::vector
<DiffOutput
> Diffs
= findDifferences(IFLHS
, IFRHS
);
562 printDifferences(OS
, Diffs
, 0);