brintos

brintos / llvm-project-archived public Read only

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