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