1 //===- MinimalTypeDumper.cpp ---------------------------------- *- C++ --*-===//
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 #include "MinimalTypeDumper.h"
10 
11 #include "FormatUtil.h"
12 #include "LinePrinter.h"
13 #include "TypeReferenceTracker.h"
14 
15 #include "llvm-pdbutil.h"
16 #include "llvm/DebugInfo/CodeView/CVRecord.h"
17 #include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
18 #include "llvm/DebugInfo/CodeView/CodeView.h"
19 #include "llvm/DebugInfo/CodeView/Formatters.h"
20 #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
21 #include "llvm/DebugInfo/CodeView/TypeRecord.h"
22 #include "llvm/DebugInfo/PDB/Native/TpiHashing.h"
23 #include "llvm/DebugInfo/PDB/Native/TpiStream.h"
24 #include "llvm/Support/FormatVariadic.h"
25 #include "llvm/Support/MathExtras.h"
26 
27 using namespace llvm;
28 using namespace llvm::codeview;
29 using namespace llvm::pdb;
30 
formatClassOptions(uint32_t IndentLevel,ClassOptions Options,TpiStream * Stream,TypeIndex CurrentTypeIndex)31 static std::string formatClassOptions(uint32_t IndentLevel,
32                                       ClassOptions Options, TpiStream *Stream,
33                                       TypeIndex CurrentTypeIndex) {
34   std::vector<std::string> Opts;
35 
36   if (Stream && Stream->supportsTypeLookup() &&
37       !opts::dump::DontResolveForwardRefs &&
38       ((Options & ClassOptions::ForwardReference) != ClassOptions::None)) {
39     // If we're able to resolve forward references, do that.
40     Expected<TypeIndex> ETI =
41         Stream->findFullDeclForForwardRef(CurrentTypeIndex);
42     if (!ETI) {
43       consumeError(ETI.takeError());
44       PUSH_FLAG(ClassOptions, ForwardReference, Options, "forward ref (??\?)");
45     } else {
46       const char *Direction = (*ETI == CurrentTypeIndex)
47                                   ? "="
48                                   : ((*ETI < CurrentTypeIndex) ? "<-" : "->");
49       std::string Formatted =
50           formatv("forward ref ({0} {1})", Direction, *ETI).str();
51       PUSH_FLAG(ClassOptions, ForwardReference, Options, std::move(Formatted));
52     }
53   } else {
54     PUSH_FLAG(ClassOptions, ForwardReference, Options, "forward ref");
55   }
56 
57   PUSH_FLAG(ClassOptions, HasConstructorOrDestructor, Options,
58             "has ctor / dtor");
59   PUSH_FLAG(ClassOptions, ContainsNestedClass, Options,
60             "contains nested class");
61   PUSH_FLAG(ClassOptions, HasConversionOperator, Options,
62             "conversion operator");
63   PUSH_FLAG(ClassOptions, HasUniqueName, Options, "has unique name");
64   PUSH_FLAG(ClassOptions, Intrinsic, Options, "intrin");
65   PUSH_FLAG(ClassOptions, Nested, Options, "is nested");
66   PUSH_FLAG(ClassOptions, HasOverloadedOperator, Options,
67             "overloaded operator");
68   PUSH_FLAG(ClassOptions, HasOverloadedAssignmentOperator, Options,
69             "overloaded operator=");
70   PUSH_FLAG(ClassOptions, Packed, Options, "packed");
71   PUSH_FLAG(ClassOptions, Scoped, Options, "scoped");
72   PUSH_FLAG(ClassOptions, Sealed, Options, "sealed");
73 
74   return typesetItemList(Opts, 4, IndentLevel, " | ");
75 }
76 
pointerOptions(PointerOptions Options)77 static std::string pointerOptions(PointerOptions Options) {
78   std::vector<std::string> Opts;
79   PUSH_FLAG(PointerOptions, Flat32, Options, "flat32");
80   PUSH_FLAG(PointerOptions, Volatile, Options, "volatile");
81   PUSH_FLAG(PointerOptions, Const, Options, "const");
82   PUSH_FLAG(PointerOptions, Unaligned, Options, "unaligned");
83   PUSH_FLAG(PointerOptions, Restrict, Options, "restrict");
84   PUSH_FLAG(PointerOptions, WinRTSmartPointer, Options, "winrt");
85   if (Opts.empty())
86     return "None";
87   return join(Opts, " | ");
88 }
89 
modifierOptions(ModifierOptions Options)90 static std::string modifierOptions(ModifierOptions Options) {
91   std::vector<std::string> Opts;
92   PUSH_FLAG(ModifierOptions, Const, Options, "const");
93   PUSH_FLAG(ModifierOptions, Volatile, Options, "volatile");
94   PUSH_FLAG(ModifierOptions, Unaligned, Options, "unaligned");
95   if (Opts.empty())
96     return "None";
97   return join(Opts, " | ");
98 }
99 
formatCallingConvention(CallingConvention Convention)100 static std::string formatCallingConvention(CallingConvention Convention) {
101   switch (Convention) {
102     RETURN_CASE(CallingConvention, AlphaCall, "alphacall");
103     RETURN_CASE(CallingConvention, AM33Call, "am33call");
104     RETURN_CASE(CallingConvention, ArmCall, "armcall");
105     RETURN_CASE(CallingConvention, ClrCall, "clrcall");
106     RETURN_CASE(CallingConvention, FarC, "far cdecl");
107     RETURN_CASE(CallingConvention, FarFast, "far fastcall");
108     RETURN_CASE(CallingConvention, FarPascal, "far pascal");
109     RETURN_CASE(CallingConvention, FarStdCall, "far stdcall");
110     RETURN_CASE(CallingConvention, FarSysCall, "far syscall");
111     RETURN_CASE(CallingConvention, Generic, "generic");
112     RETURN_CASE(CallingConvention, Inline, "inline");
113     RETURN_CASE(CallingConvention, M32RCall, "m32rcall");
114     RETURN_CASE(CallingConvention, MipsCall, "mipscall");
115     RETURN_CASE(CallingConvention, NearC, "cdecl");
116     RETURN_CASE(CallingConvention, NearFast, "fastcall");
117     RETURN_CASE(CallingConvention, NearPascal, "pascal");
118     RETURN_CASE(CallingConvention, NearStdCall, "stdcall");
119     RETURN_CASE(CallingConvention, NearSysCall, "near syscall");
120     RETURN_CASE(CallingConvention, NearVector, "vectorcall");
121     RETURN_CASE(CallingConvention, PpcCall, "ppccall");
122     RETURN_CASE(CallingConvention, SHCall, "shcall");
123     RETURN_CASE(CallingConvention, SH5Call, "sh5call");
124     RETURN_CASE(CallingConvention, ThisCall, "thiscall");
125     RETURN_CASE(CallingConvention, TriCall, "tricall");
126   }
127   return formatUnknownEnum(Convention);
128 }
129 
formatPointerMode(PointerMode Mode)130 static std::string formatPointerMode(PointerMode Mode) {
131   switch (Mode) {
132     RETURN_CASE(PointerMode, LValueReference, "ref");
133     RETURN_CASE(PointerMode, Pointer, "pointer");
134     RETURN_CASE(PointerMode, PointerToDataMember, "data member pointer");
135     RETURN_CASE(PointerMode, PointerToMemberFunction, "member fn pointer");
136     RETURN_CASE(PointerMode, RValueReference, "rvalue ref");
137   }
138   return formatUnknownEnum(Mode);
139 }
140 
memberAccess(MemberAccess Access)141 static std::string memberAccess(MemberAccess Access) {
142   switch (Access) {
143     RETURN_CASE(MemberAccess, None, "");
144     RETURN_CASE(MemberAccess, Private, "private");
145     RETURN_CASE(MemberAccess, Protected, "protected");
146     RETURN_CASE(MemberAccess, Public, "public");
147   }
148   return formatUnknownEnum(Access);
149 }
150 
methodKind(MethodKind Kind)151 static std::string methodKind(MethodKind Kind) {
152   switch (Kind) {
153     RETURN_CASE(MethodKind, Vanilla, "");
154     RETURN_CASE(MethodKind, Virtual, "virtual");
155     RETURN_CASE(MethodKind, Static, "static");
156     RETURN_CASE(MethodKind, Friend, "friend");
157     RETURN_CASE(MethodKind, IntroducingVirtual, "intro virtual");
158     RETURN_CASE(MethodKind, PureVirtual, "pure virtual");
159     RETURN_CASE(MethodKind, PureIntroducingVirtual, "pure intro virtual");
160   }
161   return formatUnknownEnum(Kind);
162 }
163 
pointerKind(PointerKind Kind)164 static std::string pointerKind(PointerKind Kind) {
165   switch (Kind) {
166     RETURN_CASE(PointerKind, Near16, "ptr16");
167     RETURN_CASE(PointerKind, Far16, "far ptr16");
168     RETURN_CASE(PointerKind, Huge16, "huge ptr16");
169     RETURN_CASE(PointerKind, BasedOnSegment, "segment based");
170     RETURN_CASE(PointerKind, BasedOnValue, "value based");
171     RETURN_CASE(PointerKind, BasedOnSegmentValue, "segment value based");
172     RETURN_CASE(PointerKind, BasedOnAddress, "address based");
173     RETURN_CASE(PointerKind, BasedOnSegmentAddress, "segment address based");
174     RETURN_CASE(PointerKind, BasedOnType, "type based");
175     RETURN_CASE(PointerKind, BasedOnSelf, "self based");
176     RETURN_CASE(PointerKind, Near32, "ptr32");
177     RETURN_CASE(PointerKind, Far32, "far ptr32");
178     RETURN_CASE(PointerKind, Near64, "ptr64");
179   }
180   return formatUnknownEnum(Kind);
181 }
182 
memberAttributes(const MemberAttributes & Attrs)183 static std::string memberAttributes(const MemberAttributes &Attrs) {
184   std::vector<std::string> Opts;
185   std::string Access = memberAccess(Attrs.getAccess());
186   std::string Kind = methodKind(Attrs.getMethodKind());
187   if (!Access.empty())
188     Opts.push_back(Access);
189   if (!Kind.empty())
190     Opts.push_back(Kind);
191   MethodOptions Flags = Attrs.getFlags();
192   PUSH_FLAG(MethodOptions, Pseudo, Flags, "pseudo");
193   PUSH_FLAG(MethodOptions, NoInherit, Flags, "noinherit");
194   PUSH_FLAG(MethodOptions, NoConstruct, Flags, "noconstruct");
195   PUSH_FLAG(MethodOptions, CompilerGenerated, Flags, "compiler-generated");
196   PUSH_FLAG(MethodOptions, Sealed, Flags, "sealed");
197   return join(Opts, " ");
198 }
199 
formatPointerAttrs(const PointerRecord & Record)200 static std::string formatPointerAttrs(const PointerRecord &Record) {
201   PointerMode Mode = Record.getMode();
202   PointerOptions Opts = Record.getOptions();
203   PointerKind Kind = Record.getPointerKind();
204   return std::string(formatv("mode = {0}, opts = {1}, kind = {2}",
205                              formatPointerMode(Mode), pointerOptions(Opts),
206                              pointerKind(Kind)));
207 }
208 
formatFunctionOptions(FunctionOptions Options)209 static std::string formatFunctionOptions(FunctionOptions Options) {
210   std::vector<std::string> Opts;
211 
212   PUSH_FLAG(FunctionOptions, CxxReturnUdt, Options, "returns cxx udt");
213   PUSH_FLAG(FunctionOptions, ConstructorWithVirtualBases, Options,
214             "constructor with virtual bases");
215   PUSH_FLAG(FunctionOptions, Constructor, Options, "constructor");
216   if (Opts.empty())
217     return "None";
218   return join(Opts, " | ");
219 }
220 
visitTypeBegin(CVType & Record,TypeIndex Index)221 Error MinimalTypeDumpVisitor::visitTypeBegin(CVType &Record, TypeIndex Index) {
222   CurrentTypeIndex = Index;
223   // formatLine puts the newline at the beginning, so we use formatLine here
224   // to start a new line, and then individual visit methods use format to
225   // append to the existing line.
226   P.formatLine("{0} | {1} [size = {2}",
227                fmt_align(Index, AlignStyle::Right, Width),
228                formatTypeLeafKind(Record.kind()), Record.length());
229   if (Hashes) {
230     std::string H;
231     if (Index.toArrayIndex() >= HashValues.size()) {
232       H = "(not present)";
233     } else {
234       uint32_t Hash = HashValues[Index.toArrayIndex()];
235       Expected<uint32_t> MaybeHash = hashTypeRecord(Record);
236       if (!MaybeHash)
237         return MaybeHash.takeError();
238       uint32_t OurHash = *MaybeHash;
239       OurHash %= NumHashBuckets;
240       if (Hash == OurHash)
241         H = "0x" + utohexstr(Hash);
242       else
243         H = "0x" + utohexstr(Hash) + ", our hash = 0x" + utohexstr(OurHash);
244     }
245     P.format(", hash = {0}", H);
246   }
247   if (RefTracker) {
248     if (RefTracker->isTypeReferenced(Index))
249       P.format(", referenced");
250     else
251       P.format(", unreferenced");
252   }
253   P.format("]");
254   P.Indent(Width + 3);
255   return Error::success();
256 }
257 
visitTypeEnd(CVType & Record)258 Error MinimalTypeDumpVisitor::visitTypeEnd(CVType &Record) {
259   P.Unindent(Width + 3);
260   if (RecordBytes) {
261     AutoIndent Indent(P, 9);
262     P.formatBinary("Bytes", Record.RecordData, 0);
263   }
264   return Error::success();
265 }
266 
visitMemberBegin(CVMemberRecord & Record)267 Error MinimalTypeDumpVisitor::visitMemberBegin(CVMemberRecord &Record) {
268   P.formatLine("- {0}", formatTypeLeafKind(Record.Kind));
269   return Error::success();
270 }
271 
visitMemberEnd(CVMemberRecord & Record)272 Error MinimalTypeDumpVisitor::visitMemberEnd(CVMemberRecord &Record) {
273   if (RecordBytes) {
274     AutoIndent Indent(P, 2);
275     P.formatBinary("Bytes", Record.Data, 0);
276   }
277   return Error::success();
278 }
279 
getTypeName(TypeIndex TI) const280 StringRef MinimalTypeDumpVisitor::getTypeName(TypeIndex TI) const {
281   if (TI.isNoneType())
282     return "";
283   return Types.getTypeName(TI);
284 }
285 
visitKnownRecord(CVType & CVR,FieldListRecord & FieldList)286 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
287                                                FieldListRecord &FieldList) {
288   if (auto EC = codeview::visitMemberRecordStream(FieldList.Data, *this))
289     return EC;
290 
291   return Error::success();
292 }
293 
visitKnownRecord(CVType & CVR,StringIdRecord & String)294 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
295                                                StringIdRecord &String) {
296   P.format(" ID: {0}, String: {1}", String.getId(), String.getString());
297   return Error::success();
298 }
299 
visitKnownRecord(CVType & CVR,ArgListRecord & Args)300 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
301                                                ArgListRecord &Args) {
302   auto Indices = Args.getIndices();
303   if (Indices.empty())
304     return Error::success();
305 
306   auto Max = std::max_element(Indices.begin(), Indices.end());
307   uint32_t W = NumDigits(Max->getIndex()) + 2;
308 
309   for (auto I : Indices)
310     P.formatLine("{0}: `{1}`", fmt_align(I, AlignStyle::Right, W),
311                  getTypeName(I));
312   return Error::success();
313 }
314 
visitKnownRecord(CVType & CVR,StringListRecord & Strings)315 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
316                                                StringListRecord &Strings) {
317   auto Indices = Strings.getIndices();
318   if (Indices.empty())
319     return Error::success();
320 
321   auto Max = std::max_element(Indices.begin(), Indices.end());
322   uint32_t W = NumDigits(Max->getIndex()) + 2;
323 
324   for (auto I : Indices)
325     P.formatLine("{0}: `{1}`", fmt_align(I, AlignStyle::Right, W),
326                  getTypeName(I));
327   return Error::success();
328 }
329 
visitKnownRecord(CVType & CVR,ClassRecord & Class)330 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
331                                                ClassRecord &Class) {
332   P.format(" `{0}`", Class.Name);
333   if (Class.hasUniqueName())
334     P.formatLine("unique name: `{0}`", Class.UniqueName);
335   P.formatLine("vtable: {0}, base list: {1}, field list: {2}",
336                Class.VTableShape, Class.DerivationList, Class.FieldList);
337   P.formatLine("options: {0}, sizeof {1}",
338                formatClassOptions(P.getIndentLevel(), Class.Options, Stream,
339                                   CurrentTypeIndex),
340                Class.Size);
341   return Error::success();
342 }
343 
visitKnownRecord(CVType & CVR,UnionRecord & Union)344 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
345                                                UnionRecord &Union) {
346   P.format(" `{0}`", Union.Name);
347   if (Union.hasUniqueName())
348     P.formatLine("unique name: `{0}`", Union.UniqueName);
349   P.formatLine("field list: {0}", Union.FieldList);
350   P.formatLine("options: {0}, sizeof {1}",
351                formatClassOptions(P.getIndentLevel(), Union.Options, Stream,
352                                   CurrentTypeIndex),
353                Union.Size);
354   return Error::success();
355 }
356 
visitKnownRecord(CVType & CVR,EnumRecord & Enum)357 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, EnumRecord &Enum) {
358   P.format(" `{0}`", Enum.Name);
359   if (Enum.hasUniqueName())
360     P.formatLine("unique name: `{0}`", Enum.UniqueName);
361   P.formatLine("field list: {0}, underlying type: {1}", Enum.FieldList,
362                Enum.UnderlyingType);
363   P.formatLine("options: {0}",
364                formatClassOptions(P.getIndentLevel(), Enum.Options, Stream,
365                                   CurrentTypeIndex));
366   return Error::success();
367 }
368 
visitKnownRecord(CVType & CVR,ArrayRecord & AT)369 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, ArrayRecord &AT) {
370   if (AT.Name.empty()) {
371     P.formatLine("size: {0}, index type: {1}, element type: {2}", AT.Size,
372                  AT.IndexType, AT.ElementType);
373   } else {
374     P.formatLine("name: {0}, size: {1}, index type: {2}, element type: {3}",
375                  AT.Name, AT.Size, AT.IndexType, AT.ElementType);
376   }
377   return Error::success();
378 }
379 
visitKnownRecord(CVType & CVR,VFTableRecord & VFT)380 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
381                                                VFTableRecord &VFT) {
382   P.formatLine("offset: {0}, complete class: {1}, overridden vftable: {2}",
383                VFT.VFPtrOffset, VFT.CompleteClass, VFT.OverriddenVFTable);
384   P.formatLine("method names: ");
385   if (!VFT.MethodNames.empty()) {
386     std::string Sep =
387         formatv("\n{0}",
388                 fmt_repeat(' ', P.getIndentLevel() + strlen("method names: ")))
389             .str();
390     P.print(join(VFT.MethodNames, Sep));
391   }
392   return Error::success();
393 }
394 
visitKnownRecord(CVType & CVR,MemberFuncIdRecord & Id)395 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
396                                                MemberFuncIdRecord &Id) {
397   P.formatLine("name = {0}, type = {1}, class type = {2}", Id.Name,
398                Id.FunctionType, Id.ClassType);
399   return Error::success();
400 }
401 
visitKnownRecord(CVType & CVR,ProcedureRecord & Proc)402 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
403                                                ProcedureRecord &Proc) {
404   P.formatLine("return type = {0}, # args = {1}, param list = {2}",
405                Proc.ReturnType, Proc.ParameterCount, Proc.ArgumentList);
406   P.formatLine("calling conv = {0}, options = {1}",
407                formatCallingConvention(Proc.CallConv),
408                formatFunctionOptions(Proc.Options));
409   return Error::success();
410 }
411 
visitKnownRecord(CVType & CVR,MemberFunctionRecord & MF)412 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
413                                                MemberFunctionRecord &MF) {
414   P.formatLine("return type = {0}, # args = {1}, param list = {2}",
415                MF.ReturnType, MF.ParameterCount, MF.ArgumentList);
416   P.formatLine("class type = {0}, this type = {1}, this adjust = {2}",
417                MF.ClassType, MF.ThisType, MF.ThisPointerAdjustment);
418   P.formatLine("calling conv = {0}, options = {1}",
419                formatCallingConvention(MF.CallConv),
420                formatFunctionOptions(MF.Options));
421   return Error::success();
422 }
423 
visitKnownRecord(CVType & CVR,FuncIdRecord & Func)424 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
425                                                FuncIdRecord &Func) {
426   P.formatLine("name = {0}, type = {1}, parent scope = {2}", Func.Name,
427                Func.FunctionType, Func.ParentScope);
428   return Error::success();
429 }
430 
visitKnownRecord(CVType & CVR,TypeServer2Record & TS)431 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
432                                                TypeServer2Record &TS) {
433   P.formatLine("name = {0}, age = {1}, guid = {2}", TS.Name, TS.Age, TS.Guid);
434   return Error::success();
435 }
436 
visitKnownRecord(CVType & CVR,PointerRecord & Ptr)437 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
438                                                PointerRecord &Ptr) {
439   P.formatLine("referent = {0}, {1}", Ptr.ReferentType,
440                formatPointerAttrs(Ptr));
441   return Error::success();
442 }
443 
visitKnownRecord(CVType & CVR,ModifierRecord & Mod)444 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
445                                                ModifierRecord &Mod) {
446   P.formatLine("referent = {0}, modifiers = {1}", Mod.ModifiedType,
447                modifierOptions(Mod.Modifiers));
448   return Error::success();
449 }
450 
visitKnownRecord(CVType & CVR,VFTableShapeRecord & Shape)451 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
452                                                VFTableShapeRecord &Shape) {
453   return Error::success();
454 }
455 
visitKnownRecord(CVType & CVR,UdtModSourceLineRecord & U)456 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
457                                                UdtModSourceLineRecord &U) {
458   P.formatLine("udt = {0}, mod = {1}, file = {2}, line = {3}", U.UDT, U.Module,
459                U.SourceFile.getIndex(), U.LineNumber);
460   return Error::success();
461 }
462 
visitKnownRecord(CVType & CVR,UdtSourceLineRecord & U)463 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
464                                                UdtSourceLineRecord &U) {
465   P.formatLine("udt = {0}, file = {1}, line = {2}", U.UDT,
466                U.SourceFile.getIndex(), U.LineNumber);
467   return Error::success();
468 }
469 
visitKnownRecord(CVType & CVR,BitFieldRecord & BF)470 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
471                                                BitFieldRecord &BF) {
472   P.formatLine("type = {0}, bit offset = {1}, # bits = {2}", BF.Type,
473                BF.BitOffset, BF.BitSize);
474   return Error::success();
475 }
476 
visitKnownRecord(CVType & CVR,MethodOverloadListRecord & Overloads)477 Error MinimalTypeDumpVisitor::visitKnownRecord(
478     CVType &CVR, MethodOverloadListRecord &Overloads) {
479   for (auto &M : Overloads.Methods)
480     P.formatLine("- Method [type = {0}, vftable offset = {1}, attrs = {2}]",
481                  M.Type, M.VFTableOffset, memberAttributes(M.Attrs));
482   return Error::success();
483 }
484 
visitKnownRecord(CVType & CVR,BuildInfoRecord & BI)485 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
486                                                BuildInfoRecord &BI) {
487   auto Indices = BI.ArgIndices;
488   if (Indices.empty())
489     return Error::success();
490 
491   auto Max = std::max_element(Indices.begin(), Indices.end());
492   uint32_t W = NumDigits(Max->getIndex()) + 2;
493 
494   for (auto I : Indices)
495     P.formatLine("{0}: `{1}`", fmt_align(I, AlignStyle::Right, W),
496                  getTypeName(I));
497   return Error::success();
498 }
499 
visitKnownRecord(CVType & CVR,LabelRecord & R)500 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, LabelRecord &R) {
501   std::string Type = (R.Mode == LabelType::Far) ? "far" : "near";
502   P.format(" type = {0}", Type);
503   return Error::success();
504 }
505 
visitKnownRecord(CVType & CVR,PrecompRecord & Precomp)506 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
507                                                PrecompRecord &Precomp) {
508   P.format(" start index = {0:X+}, types count = {1:X+}, signature = {2:X+},"
509            " precomp path = {3}",
510            Precomp.StartTypeIndex, Precomp.TypesCount, Precomp.Signature,
511            Precomp.PrecompFilePath);
512   return Error::success();
513 }
514 
visitKnownRecord(CVType & CVR,EndPrecompRecord & EP)515 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
516                                                EndPrecompRecord &EP) {
517   P.format(" signature = {0:X+}", EP.Signature);
518   return Error::success();
519 }
520 
visitKnownMember(CVMemberRecord & CVR,NestedTypeRecord & Nested)521 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
522                                                NestedTypeRecord &Nested) {
523   P.format(" [name = `{0}`, parent = {1}]", Nested.Name, Nested.Type);
524   return Error::success();
525 }
526 
visitKnownMember(CVMemberRecord & CVR,OneMethodRecord & Method)527 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
528                                                OneMethodRecord &Method) {
529   P.format(" [name = `{0}`]", Method.Name);
530   AutoIndent Indent(P);
531   P.formatLine("type = {0}, vftable offset = {1}, attrs = {2}", Method.Type,
532                Method.VFTableOffset, memberAttributes(Method.Attrs));
533   return Error::success();
534 }
535 
visitKnownMember(CVMemberRecord & CVR,OverloadedMethodRecord & Method)536 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
537                                                OverloadedMethodRecord &Method) {
538   P.format(" [name = `{0}`, # overloads = {1}, overload list = {2}]",
539            Method.Name, Method.NumOverloads, Method.MethodList);
540   return Error::success();
541 }
542 
visitKnownMember(CVMemberRecord & CVR,DataMemberRecord & Field)543 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
544                                                DataMemberRecord &Field) {
545   P.format(" [name = `{0}`, Type = {1}, offset = {2}, attrs = {3}]", Field.Name,
546            Field.Type, Field.FieldOffset, memberAttributes(Field.Attrs));
547   return Error::success();
548 }
549 
visitKnownMember(CVMemberRecord & CVR,StaticDataMemberRecord & Field)550 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
551                                                StaticDataMemberRecord &Field) {
552   P.format(" [name = `{0}`, type = {1}, attrs = {2}]", Field.Name, Field.Type,
553            memberAttributes(Field.Attrs));
554   return Error::success();
555 }
556 
visitKnownMember(CVMemberRecord & CVR,EnumeratorRecord & Enum)557 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
558                                                EnumeratorRecord &Enum) {
559   P.format(" [{0} = {1}]", Enum.Name,
560            Enum.Value.toString(10, Enum.Value.isSigned()));
561   return Error::success();
562 }
563 
visitKnownMember(CVMemberRecord & CVR,BaseClassRecord & Base)564 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
565                                                BaseClassRecord &Base) {
566   AutoIndent Indent(P);
567   P.formatLine("type = {0}, offset = {1}, attrs = {2}", Base.Type, Base.Offset,
568                memberAttributes(Base.Attrs));
569   return Error::success();
570 }
571 
visitKnownMember(CVMemberRecord & CVR,VirtualBaseClassRecord & Base)572 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
573                                                VirtualBaseClassRecord &Base) {
574   AutoIndent Indent(P);
575   P.formatLine(
576       "base = {0}, vbptr = {1}, vbptr offset = {2}, vtable index = {3}",
577       Base.BaseType, Base.VBPtrType, Base.VBPtrOffset, Base.VTableIndex);
578   P.formatLine("attrs = {0}", memberAttributes(Base.Attrs));
579   return Error::success();
580 }
581 
visitKnownMember(CVMemberRecord & CVR,ListContinuationRecord & Cont)582 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
583                                                ListContinuationRecord &Cont) {
584   P.format(" continuation = {0}", Cont.ContinuationIndex);
585   return Error::success();
586 }
587 
visitKnownMember(CVMemberRecord & CVR,VFPtrRecord & VFP)588 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
589                                                VFPtrRecord &VFP) {
590   P.format(" type = {0}", VFP.Type);
591   return Error::success();
592 }
593