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
27using namespace llvm;
28using namespace llvm::codeview;
29using namespace llvm::pdb;
30
31static 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
77static 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
90static 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
100static 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
130static 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
141static 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
151static 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
164static 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
183static 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
200static std::string formatPointerAttrs(const PointerRecord &Record) {
201  PointerMode Mode = Record.getMode();
202  PointerOptions Opts = Record.getOptions();
203  PointerKind Kind = Record.getPointerKind();
204  return formatv("mode = {0}, opts = {1}, kind = {2}", formatPointerMode(Mode),
205                 pointerOptions(Opts), pointerKind(Kind));
206}
207
208static std::string formatFunctionOptions(FunctionOptions Options) {
209  std::vector<std::string> Opts;
210
211  PUSH_FLAG(FunctionOptions, CxxReturnUdt, Options, "returns cxx udt");
212  PUSH_FLAG(FunctionOptions, ConstructorWithVirtualBases, Options,
213            "constructor with virtual bases");
214  PUSH_FLAG(FunctionOptions, Constructor, Options, "constructor");
215  if (Opts.empty())
216    return "None";
217  return join(Opts, " | ");
218}
219
220Error MinimalTypeDumpVisitor::visitTypeBegin(CVType &Record, TypeIndex Index) {
221  CurrentTypeIndex = Index;
222  // formatLine puts the newline at the beginning, so we use formatLine here
223  // to start a new line, and then individual visit methods use format to
224  // append to the existing line.
225  P.formatLine("{0} | {1} [size = {2}",
226               fmt_align(Index, AlignStyle::Right, Width),
227               formatTypeLeafKind(Record.kind()), Record.length());
228  if (Hashes) {
229    std::string H;
230    if (Index.toArrayIndex() >= HashValues.size()) {
231      H = "(not present)";
232    } else {
233      uint32_t Hash = HashValues[Index.toArrayIndex()];
234      Expected<uint32_t> MaybeHash = hashTypeRecord(Record);
235      if (!MaybeHash)
236        return MaybeHash.takeError();
237      uint32_t OurHash = *MaybeHash;
238      OurHash %= NumHashBuckets;
239      if (Hash == OurHash)
240        H = "0x" + utohexstr(Hash);
241      else
242        H = "0x" + utohexstr(Hash) + ", our hash = 0x" + utohexstr(OurHash);
243    }
244    P.format(", hash = {0}", H);
245  }
246  if (RefTracker) {
247    if (RefTracker->isTypeReferenced(Index))
248      P.format(", referenced");
249    else
250      P.format(", unreferenced");
251  }
252  P.format("]");
253  P.Indent(Width + 3);
254  return Error::success();
255}
256
257Error MinimalTypeDumpVisitor::visitTypeEnd(CVType &Record) {
258  P.Unindent(Width + 3);
259  if (RecordBytes) {
260    AutoIndent Indent(P, 9);
261    P.formatBinary("Bytes", Record.RecordData, 0);
262  }
263  return Error::success();
264}
265
266Error MinimalTypeDumpVisitor::visitMemberBegin(CVMemberRecord &Record) {
267  P.formatLine("- {0}", formatTypeLeafKind(Record.Kind));
268  return Error::success();
269}
270
271Error MinimalTypeDumpVisitor::visitMemberEnd(CVMemberRecord &Record) {
272  if (RecordBytes) {
273    AutoIndent Indent(P, 2);
274    P.formatBinary("Bytes", Record.Data, 0);
275  }
276  return Error::success();
277}
278
279StringRef MinimalTypeDumpVisitor::getTypeName(TypeIndex TI) const {
280  if (TI.isNoneType())
281    return "";
282  return Types.getTypeName(TI);
283}
284
285Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
286                                               FieldListRecord &FieldList) {
287  if (auto EC = codeview::visitMemberRecordStream(FieldList.Data, *this))
288    return EC;
289
290  return Error::success();
291}
292
293Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
294                                               StringIdRecord &String) {
295  P.format(" ID: {0}, String: {1}", String.getId(), String.getString());
296  return Error::success();
297}
298
299Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
300                                               ArgListRecord &Args) {
301  auto Indices = Args.getIndices();
302  if (Indices.empty())
303    return Error::success();
304
305  auto Max = std::max_element(Indices.begin(), Indices.end());
306  uint32_t W = NumDigits(Max->getIndex()) + 2;
307
308  for (auto I : Indices)
309    P.formatLine("{0}: `{1}`", fmt_align(I, AlignStyle::Right, W),
310                 getTypeName(I));
311  return Error::success();
312}
313
314Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
315                                               StringListRecord &Strings) {
316  auto Indices = Strings.getIndices();
317  if (Indices.empty())
318    return Error::success();
319
320  auto Max = std::max_element(Indices.begin(), Indices.end());
321  uint32_t W = NumDigits(Max->getIndex()) + 2;
322
323  for (auto I : Indices)
324    P.formatLine("{0}: `{1}`", fmt_align(I, AlignStyle::Right, W),
325                 getTypeName(I));
326  return Error::success();
327}
328
329Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
330                                               ClassRecord &Class) {
331  P.format(" `{0}`", Class.Name);
332  if (Class.hasUniqueName())
333    P.formatLine("unique name: `{0}`", Class.UniqueName);
334  P.formatLine("vtable: {0}, base list: {1}, field list: {2}",
335               Class.VTableShape, Class.DerivationList, Class.FieldList);
336  P.formatLine("options: {0}, sizeof {1}",
337               formatClassOptions(P.getIndentLevel(), Class.Options, Stream,
338                                  CurrentTypeIndex),
339               Class.Size);
340  return Error::success();
341}
342
343Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
344                                               UnionRecord &Union) {
345  P.format(" `{0}`", Union.Name);
346  if (Union.hasUniqueName())
347    P.formatLine("unique name: `{0}`", Union.UniqueName);
348  P.formatLine("field list: {0}", Union.FieldList);
349  P.formatLine("options: {0}, sizeof {1}",
350               formatClassOptions(P.getIndentLevel(), Union.Options, Stream,
351                                  CurrentTypeIndex),
352               Union.Size);
353  return Error::success();
354}
355
356Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, EnumRecord &Enum) {
357  P.format(" `{0}`", Enum.Name);
358  if (Enum.hasUniqueName())
359    P.formatLine("unique name: `{0}`", Enum.UniqueName);
360  P.formatLine("field list: {0}, underlying type: {1}", Enum.FieldList,
361               Enum.UnderlyingType);
362  P.formatLine("options: {0}",
363               formatClassOptions(P.getIndentLevel(), Enum.Options, Stream,
364                                  CurrentTypeIndex));
365  return Error::success();
366}
367
368Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, ArrayRecord &AT) {
369  if (AT.Name.empty()) {
370    P.formatLine("size: {0}, index type: {1}, element type: {2}", AT.Size,
371                 AT.IndexType, AT.ElementType);
372  } else {
373    P.formatLine("name: {0}, size: {1}, index type: {2}, element type: {3}",
374                 AT.Name, AT.Size, AT.IndexType, AT.ElementType);
375  }
376  return Error::success();
377}
378
379Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
380                                               VFTableRecord &VFT) {
381  P.formatLine("offset: {0}, complete class: {1}, overridden vftable: {2}",
382               VFT.VFPtrOffset, VFT.CompleteClass, VFT.OverriddenVFTable);
383  P.formatLine("method names: ");
384  if (!VFT.MethodNames.empty()) {
385    std::string Sep =
386        formatv("\n{0}",
387                fmt_repeat(' ', P.getIndentLevel() + strlen("method names: ")))
388            .str();
389    P.print(join(VFT.MethodNames, Sep));
390  }
391  return Error::success();
392}
393
394Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
395                                               MemberFuncIdRecord &Id) {
396  P.formatLine("name = {0}, type = {1}, class type = {2}", Id.Name,
397               Id.FunctionType, Id.ClassType);
398  return Error::success();
399}
400
401Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
402                                               ProcedureRecord &Proc) {
403  P.formatLine("return type = {0}, # args = {1}, param list = {2}",
404               Proc.ReturnType, Proc.ParameterCount, Proc.ArgumentList);
405  P.formatLine("calling conv = {0}, options = {1}",
406               formatCallingConvention(Proc.CallConv),
407               formatFunctionOptions(Proc.Options));
408  return Error::success();
409}
410
411Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
412                                               MemberFunctionRecord &MF) {
413  P.formatLine("return type = {0}, # args = {1}, param list = {2}",
414               MF.ReturnType, MF.ParameterCount, MF.ArgumentList);
415  P.formatLine("class type = {0}, this type = {1}, this adjust = {2}",
416               MF.ClassType, MF.ThisType, MF.ThisPointerAdjustment);
417  P.formatLine("calling conv = {0}, options = {1}",
418               formatCallingConvention(MF.CallConv),
419               formatFunctionOptions(MF.Options));
420  return Error::success();
421}
422
423Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
424                                               FuncIdRecord &Func) {
425  P.formatLine("name = {0}, type = {1}, parent scope = {2}", Func.Name,
426               Func.FunctionType, Func.ParentScope);
427  return Error::success();
428}
429
430Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
431                                               TypeServer2Record &TS) {
432  P.formatLine("name = {0}, age = {1}, guid = {2}", TS.Name, TS.Age, TS.Guid);
433  return Error::success();
434}
435
436Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
437                                               PointerRecord &Ptr) {
438  P.formatLine("referent = {0}, {1}", Ptr.ReferentType,
439               formatPointerAttrs(Ptr));
440  return Error::success();
441}
442
443Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
444                                               ModifierRecord &Mod) {
445  P.formatLine("referent = {0}, modifiers = {1}", Mod.ModifiedType,
446               modifierOptions(Mod.Modifiers));
447  return Error::success();
448}
449
450Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
451                                               VFTableShapeRecord &Shape) {
452  return Error::success();
453}
454
455Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
456                                               UdtModSourceLineRecord &U) {
457  P.formatLine("udt = {0}, mod = {1}, file = {2}, line = {3}", U.UDT, U.Module,
458               U.SourceFile.getIndex(), U.LineNumber);
459  return Error::success();
460}
461
462Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
463                                               UdtSourceLineRecord &U) {
464  P.formatLine("udt = {0}, file = {1}, line = {2}", U.UDT,
465               U.SourceFile.getIndex(), U.LineNumber);
466  return Error::success();
467}
468
469Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
470                                               BitFieldRecord &BF) {
471  P.formatLine("type = {0}, bit offset = {1}, # bits = {2}", BF.Type,
472               BF.BitOffset, BF.BitSize);
473  return Error::success();
474}
475
476Error MinimalTypeDumpVisitor::visitKnownRecord(
477    CVType &CVR, MethodOverloadListRecord &Overloads) {
478  for (auto &M : Overloads.Methods)
479    P.formatLine("- Method [type = {0}, vftable offset = {1}, attrs = {2}]",
480                 M.Type, M.VFTableOffset, memberAttributes(M.Attrs));
481  return Error::success();
482}
483
484Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
485                                               BuildInfoRecord &BI) {
486  auto Indices = BI.ArgIndices;
487  if (Indices.empty())
488    return Error::success();
489
490  auto Max = std::max_element(Indices.begin(), Indices.end());
491  uint32_t W = NumDigits(Max->getIndex()) + 2;
492
493  for (auto I : Indices)
494    P.formatLine("{0}: `{1}`", fmt_align(I, AlignStyle::Right, W),
495                 getTypeName(I));
496  return Error::success();
497}
498
499Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, LabelRecord &R) {
500  std::string Type = (R.Mode == LabelType::Far) ? "far" : "near";
501  P.format(" type = {0}", Type);
502  return Error::success();
503}
504
505Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
506                                               PrecompRecord &Precomp) {
507  P.format(" start index = {0:X+}, types count = {1:X+}, signature = {2:X+},"
508           " precomp path = {3}",
509           Precomp.StartTypeIndex, Precomp.TypesCount, Precomp.Signature,
510           Precomp.PrecompFilePath);
511  return Error::success();
512}
513
514Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
515                                               EndPrecompRecord &EP) {
516  P.format(" signature = {0:X+}", EP.Signature);
517  return Error::success();
518}
519
520Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
521                                               NestedTypeRecord &Nested) {
522  P.format(" [name = `{0}`, parent = {1}]", Nested.Name, Nested.Type);
523  return Error::success();
524}
525
526Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
527                                               OneMethodRecord &Method) {
528  P.format(" [name = `{0}`]", Method.Name);
529  AutoIndent Indent(P);
530  P.formatLine("type = {0}, vftable offset = {1}, attrs = {2}", Method.Type,
531               Method.VFTableOffset, memberAttributes(Method.Attrs));
532  return Error::success();
533}
534
535Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
536                                               OverloadedMethodRecord &Method) {
537  P.format(" [name = `{0}`, # overloads = {1}, overload list = {2}]",
538           Method.Name, Method.NumOverloads, Method.MethodList);
539  return Error::success();
540}
541
542Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
543                                               DataMemberRecord &Field) {
544  P.format(" [name = `{0}`, Type = {1}, offset = {2}, attrs = {3}]", Field.Name,
545           Field.Type, Field.FieldOffset, memberAttributes(Field.Attrs));
546  return Error::success();
547}
548
549Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
550                                               StaticDataMemberRecord &Field) {
551  P.format(" [name = `{0}`, type = {1}, attrs = {2}]", Field.Name, Field.Type,
552           memberAttributes(Field.Attrs));
553  return Error::success();
554}
555
556Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
557                                               EnumeratorRecord &Enum) {
558  P.format(" [{0} = {1}]", Enum.Name,
559           Enum.Value.toString(10, Enum.Value.isSigned()));
560  return Error::success();
561}
562
563Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
564                                               BaseClassRecord &Base) {
565  AutoIndent Indent(P);
566  P.formatLine("type = {0}, offset = {1}, attrs = {2}", Base.Type, Base.Offset,
567               memberAttributes(Base.Attrs));
568  return Error::success();
569}
570
571Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
572                                               VirtualBaseClassRecord &Base) {
573  AutoIndent Indent(P);
574  P.formatLine(
575      "base = {0}, vbptr = {1}, vbptr offset = {2}, vtable index = {3}",
576      Base.BaseType, Base.VBPtrType, Base.VBPtrOffset, Base.VTableIndex);
577  P.formatLine("attrs = {0}", memberAttributes(Base.Attrs));
578  return Error::success();
579}
580
581Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
582                                               ListContinuationRecord &Cont) {
583  P.format(" continuation = {0}", Cont.ContinuationIndex);
584  return Error::success();
585}
586
587Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
588                                               VFPtrRecord &VFP) {
589  P.format(" type = {0}", VFP.Type);
590  return Error::success();
591}
592