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