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