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