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 std::string(formatv("mode = {0}, opts = {1}, kind = {2}",
205                             formatPointerMode(Mode), pointerOptions(Opts),
206                             pointerKind(Kind)));
207}
208
209static 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
221Error 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
258Error 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
267Error MinimalTypeDumpVisitor::visitMemberBegin(CVMemberRecord &Record) {
268  P.formatLine("- {0}", formatTypeLeafKind(Record.Kind));
269  return Error::success();
270}
271
272Error 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
280StringRef MinimalTypeDumpVisitor::getTypeName(TypeIndex TI) const {
281  if (TI.isNoneType())
282    return "";
283  return Types.getTypeName(TI);
284}
285
286Error 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
294Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
295                                               StringIdRecord &String) {
296  P.format(" ID: {0}, String: {1}", String.getId(), String.getString());
297  return Error::success();
298}
299
300Error 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
315Error 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
330Error 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
344Error 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
357Error 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
369Error 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
380Error 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
395Error 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
402Error 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
412Error 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
424Error 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
431Error 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
437Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
438                                               PointerRecord &Ptr) {
439  P.formatLine("referent = {0}, {1}", Ptr.ReferentType,
440               formatPointerAttrs(Ptr));
441  return Error::success();
442}
443
444Error 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
451Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
452                                               VFTableShapeRecord &Shape) {
453  return Error::success();
454}
455
456Error 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
463Error 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
470Error 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
477Error 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
485Error 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
500Error 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
506Error 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
515Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
516                                               EndPrecompRecord &EP) {
517  P.format(" signature = {0:X+}", EP.Signature);
518  return Error::success();
519}
520
521Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
522                                               NestedTypeRecord &Nested) {
523  P.format(" [name = `{0}`, parent = {1}]", Nested.Name, Nested.Type);
524  return Error::success();
525}
526
527Error 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
536Error 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
543Error 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
550Error 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
557Error 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
564Error 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
572Error 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
582Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
583                                               ListContinuationRecord &Cont) {
584  P.format(" continuation = {0}", Cont.ContinuationIndex);
585  return Error::success();
586}
587
588Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
589                                               VFPtrRecord &VFP) {
590  P.format(" type = {0}", VFP.Type);
591  return Error::success();
592}
593