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
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(ForwardRefTI: CurrentTypeIndex);
46 if (!ETI) {
47 consumeError(Err: 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(Fmt: "forward ref ({0} {1})", Vals&: Direction, Vals&: *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, IndentLevel: 4, GroupSize: IndentLevel, Sep: " | ");
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(R&: Opts, Separator: " | ");
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(R&: Opts, Separator: " | ");
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(Value: 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(Value: 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(Value: 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(Value: 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(Value: Kind);
186}
187
188static std::string memberAttributes(const MemberAttributes &Attrs) {
189 std::vector<std::string> Opts;
190 std::string Access = memberAccess(Access: Attrs.getAccess());
191 std::string Kind = methodKind(Kind: Attrs.getMethodKind());
192 if (!Access.empty())
193 Opts.push_back(x: Access);
194 if (!Kind.empty())
195 Opts.push_back(x: 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(R&: Opts, Separator: " ");
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(Fmt: "mode = {0}, opts = {1}, kind = {2}",
210 Vals: formatPointerMode(Mode), Vals: pointerOptions(Options: Opts),
211 Vals: 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(R&: Opts, Separator: " | ");
224}
225
226Error MinimalTypeDumpVisitor::visitTypeBegin(CVType &Record, TypeIndex Index) {
227 CurrentTypeIndex = Index;
228 // formatLine puts the newline at the beginning, so we use formatLine here
229 // to start a new line, and then individual visit methods use format to
230 // append to the existing line.
231 P.formatLine(Fmt: "{0} | {1} [size = {2}",
232 Items: fmt_align(Item&: Index, Where: AlignStyle::Right, Amount: Width),
233 Items: formatTypeLeafKind(K: Record.kind()), Items: 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(Type: Record);
241 if (!MaybeHash)
242 return MaybeHash.takeError();
243 uint32_t OurHash = *MaybeHash;
244 OurHash %= NumHashBuckets;
245 if (Hash == OurHash)
246 H = "0x" + utohexstr(X: Hash);
247 else
248 H = "0x" + utohexstr(X: Hash) + ", our hash = 0x" + utohexstr(X: OurHash);
249 }
250 P.format(Fmt: ", hash = {0}", Items&: H);
251 }
252 if (RefTracker) {
253 if (RefTracker->isTypeReferenced(TI: Index))
254 P.format(Fmt: ", referenced");
255 else
256 P.format(Fmt: ", unreferenced");
257 }
258 P.format(Fmt: "]");
259 P.Indent(Amount: Width + 3);
260 return Error::success();
261}
262
263Error MinimalTypeDumpVisitor::visitTypeEnd(CVType &Record) {
264 if (RecordBytes)
265 P.formatBinary(Label: "bytes", Data: Record.RecordData, StartOffset: 0);
266 P.Unindent(Amount: Width + 3);
267 return Error::success();
268}
269
270Error MinimalTypeDumpVisitor::visitUnknownType(CVType &Record) {
271 if (!RecordBytes)
272 P.formatBinary(Label: "bytes", Data: Record.RecordData, StartOffset: 0);
273 return Error::success();
274}
275
276Error MinimalTypeDumpVisitor::visitMemberBegin(CVMemberRecord &Record) {
277 P.formatLine(Fmt: "- {0}", Items: formatTypeLeafKind(K: Record.Kind));
278 return Error::success();
279}
280
281Error MinimalTypeDumpVisitor::visitMemberEnd(CVMemberRecord &Record) {
282 if (RecordBytes) {
283 AutoIndent Indent(P, 2);
284 P.formatBinary(Label: "bytes", Data: Record.Data, StartOffset: 0);
285 }
286 return Error::success();
287}
288
289Error MinimalTypeDumpVisitor::visitUnknownMember(CVMemberRecord &Record) {
290 if (!RecordBytes) {
291 AutoIndent Indent(P, 2);
292 P.formatBinary(Label: "bytes", Data: Record.Data, StartOffset: 0);
293 }
294 return Error::success();
295}
296
297StringRef MinimalTypeDumpVisitor::getTypeName(TypeIndex TI) const {
298 if (TI.isNoneType())
299 return "";
300 return Types.getTypeName(Index: TI);
301}
302
303Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
304 FieldListRecord &FieldList) {
305 if (auto EC = codeview::visitMemberRecordStream(FieldList: FieldList.Data, Callbacks&: *this))
306 return EC;
307
308 return Error::success();
309}
310
311Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
312 StringIdRecord &String) {
313 P.format(Fmt: " ID: {0}, String: {1}", Items: String.getId(), Items: String.getString());
314 return Error::success();
315}
316
317Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
318 ArgListRecord &Args) {
319 auto Indices = Args.getIndices();
320 if (Indices.empty())
321 return Error::success();
322
323 auto Max = llvm::max_element(Range&: Indices);
324 uint32_t W = NumDigitsBase10(X: Max->getIndex()) + 2;
325
326 for (auto I : Indices)
327 P.formatLine(Fmt: "{0}: `{1}`", Items: fmt_align(Item&: I, Where: AlignStyle::Right, Amount: W),
328 Items: getTypeName(TI: I));
329 return Error::success();
330}
331
332Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
333 StringListRecord &Strings) {
334 auto Indices = Strings.getIndices();
335 if (Indices.empty())
336 return Error::success();
337
338 auto Max = llvm::max_element(Range&: Indices);
339 uint32_t W = NumDigitsBase10(X: Max->getIndex()) + 2;
340
341 for (auto I : Indices)
342 P.formatLine(Fmt: "{0}: `{1}`", Items: fmt_align(Item&: I, Where: AlignStyle::Right, Amount: W),
343 Items: getTypeName(TI: I));
344 return Error::success();
345}
346
347Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
348 ClassRecord &Class) {
349 P.format(Fmt: " `{0}`", Items&: Class.Name);
350 if (Class.hasUniqueName())
351 P.formatLine(Fmt: "unique name: `{0}`", Items&: Class.UniqueName);
352 P.formatLine(Fmt: "vtable: {0}, base list: {1}, field list: {2}",
353 Items&: Class.VTableShape, Items&: Class.DerivationList, Items&: Class.FieldList);
354 P.formatLine(Fmt: "options: {0}, sizeof {1}",
355 Items: formatClassOptions(IndentLevel: P.getIndentLevel(), Options: Class.Options, Stream,
356 CurrentTypeIndex),
357 Items&: Class.Size);
358 return Error::success();
359}
360
361Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
362 UnionRecord &Union) {
363 P.format(Fmt: " `{0}`", Items&: Union.Name);
364 if (Union.hasUniqueName())
365 P.formatLine(Fmt: "unique name: `{0}`", Items&: Union.UniqueName);
366 P.formatLine(Fmt: "field list: {0}", Items&: Union.FieldList);
367 P.formatLine(Fmt: "options: {0}, sizeof {1}",
368 Items: formatClassOptions(IndentLevel: P.getIndentLevel(), Options: Union.Options, Stream,
369 CurrentTypeIndex),
370 Items&: Union.Size);
371 return Error::success();
372}
373
374Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, EnumRecord &Enum) {
375 P.format(Fmt: " `{0}`", Items&: Enum.Name);
376 if (Enum.hasUniqueName())
377 P.formatLine(Fmt: "unique name: `{0}`", Items&: Enum.UniqueName);
378 P.formatLine(Fmt: "field list: {0}, underlying type: {1}", Items&: Enum.FieldList,
379 Items&: Enum.UnderlyingType);
380 P.formatLine(Fmt: "options: {0}",
381 Items: formatClassOptions(IndentLevel: P.getIndentLevel(), Options: Enum.Options, Stream,
382 CurrentTypeIndex));
383 return Error::success();
384}
385
386Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, ArrayRecord &AT) {
387 if (AT.Name.empty()) {
388 P.formatLine(Fmt: "size: {0}, index type: {1}, element type: {2}", Items&: AT.Size,
389 Items&: AT.IndexType, Items&: AT.ElementType);
390 } else {
391 P.formatLine(Fmt: "name: {0}, size: {1}, index type: {2}, element type: {3}",
392 Items&: AT.Name, Items&: AT.Size, Items&: AT.IndexType, Items&: AT.ElementType);
393 }
394 return Error::success();
395}
396
397Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
398 VFTableRecord &VFT) {
399 P.formatLine(Fmt: "offset: {0}, complete class: {1}, overridden vftable: {2}",
400 Items&: VFT.VFPtrOffset, Items&: VFT.CompleteClass, Items&: VFT.OverriddenVFTable);
401 P.formatLine(Fmt: "method names: ");
402 if (!VFT.MethodNames.empty()) {
403 std::string Sep =
404 formatv(Fmt: "\n{0}",
405 Vals: fmt_repeat(Item: ' ', Count: P.getIndentLevel() + strlen(s: "method names: ")))
406 .str();
407 P.print(T: join(R&: VFT.MethodNames, Separator: Sep));
408 }
409 return Error::success();
410}
411
412Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
413 MemberFuncIdRecord &Id) {
414 P.formatLine(Fmt: "name = {0}, type = {1}, class type = {2}", Items&: Id.Name,
415 Items&: Id.FunctionType, Items&: Id.ClassType);
416 return Error::success();
417}
418
419Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
420 ProcedureRecord &Proc) {
421 P.formatLine(Fmt: "return type = {0}, # args = {1}, param list = {2}",
422 Items&: Proc.ReturnType, Items&: Proc.ParameterCount, Items&: Proc.ArgumentList);
423 P.formatLine(Fmt: "calling conv = {0}, options = {1}",
424 Items: formatCallingConvention(Convention: Proc.CallConv),
425 Items: formatFunctionOptions(Options: Proc.Options));
426 return Error::success();
427}
428
429Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
430 MemberFunctionRecord &MF) {
431 P.formatLine(Fmt: "return type = {0}, # args = {1}, param list = {2}",
432 Items&: MF.ReturnType, Items&: MF.ParameterCount, Items&: MF.ArgumentList);
433 P.formatLine(Fmt: "class type = {0}, this type = {1}, this adjust = {2}",
434 Items&: MF.ClassType, Items&: MF.ThisType, Items&: MF.ThisPointerAdjustment);
435 P.formatLine(Fmt: "calling conv = {0}, options = {1}",
436 Items: formatCallingConvention(Convention: MF.CallConv),
437 Items: formatFunctionOptions(Options: MF.Options));
438 return Error::success();
439}
440
441Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
442 FuncIdRecord &Func) {
443 P.formatLine(Fmt: "name = {0}, type = {1}, parent scope = {2}", Items&: Func.Name,
444 Items&: Func.FunctionType, Items&: Func.ParentScope);
445 return Error::success();
446}
447
448Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
449 TypeServer2Record &TS) {
450 P.formatLine(Fmt: "name = {0}, age = {1}, guid = {2}", Items&: TS.Name, Items&: TS.Age, Items&: TS.Guid);
451 return Error::success();
452}
453
454Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
455 PointerRecord &Ptr) {
456 P.formatLine(Fmt: "referent = {0}, {1}", Items&: Ptr.ReferentType,
457 Items: formatPointerAttrs(Record: Ptr));
458 return Error::success();
459}
460
461Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
462 ModifierRecord &Mod) {
463 P.formatLine(Fmt: "referent = {0}, modifiers = {1}", Items&: Mod.ModifiedType,
464 Items: modifierOptions(Options: Mod.Modifiers));
465 return Error::success();
466}
467
468Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
469 VFTableShapeRecord &Shape) {
470 return Error::success();
471}
472
473Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
474 UdtModSourceLineRecord &U) {
475 P.formatLine(Fmt: "udt = {0}, mod = {1}, file = {2}, line = {3}", Items&: U.UDT, Items&: U.Module,
476 Items: U.SourceFile.getIndex(), Items&: U.LineNumber);
477 return Error::success();
478}
479
480Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
481 UdtSourceLineRecord &U) {
482 P.formatLine(Fmt: "udt = {0}, file = {1}, line = {2}", Items&: U.UDT,
483 Items: U.SourceFile.getIndex(), Items&: U.LineNumber);
484 return Error::success();
485}
486
487Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
488 BitFieldRecord &BF) {
489 P.formatLine(Fmt: "type = {0}, bit offset = {1}, # bits = {2}", Items&: BF.Type,
490 Items&: BF.BitOffset, Items&: BF.BitSize);
491 return Error::success();
492}
493
494Error MinimalTypeDumpVisitor::visitKnownRecord(
495 CVType &CVR, MethodOverloadListRecord &Overloads) {
496 for (auto &M : Overloads.Methods)
497 P.formatLine(Fmt: "- Method [type = {0}, vftable offset = {1}, attrs = {2}]",
498 Items&: M.Type, Items&: M.VFTableOffset, Items: memberAttributes(Attrs: M.Attrs));
499 return Error::success();
500}
501
502Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
503 BuildInfoRecord &BI) {
504 auto Indices = BI.ArgIndices;
505 if (Indices.empty())
506 return Error::success();
507
508 auto Max = llvm::max_element(Range&: Indices);
509 uint32_t W = NumDigitsBase10(X: Max->getIndex()) + 2;
510
511 for (auto I : Indices)
512 P.formatLine(Fmt: "{0}: `{1}`", Items: fmt_align(Item&: I, Where: AlignStyle::Right, Amount: W),
513 Items: getTypeName(TI: I));
514 return Error::success();
515}
516
517Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, LabelRecord &R) {
518 std::string Type = (R.Mode == LabelType::Far) ? "far" : "near";
519 P.format(Fmt: " type = {0}", Items&: Type);
520 return Error::success();
521}
522
523Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
524 PrecompRecord &Precomp) {
525 P.format(Fmt: " start index = {0:X+}, types count = {1:X+}, signature = {2:X+},"
526 " precomp path = {3}",
527 Items&: Precomp.StartTypeIndex, Items&: Precomp.TypesCount, Items&: Precomp.Signature,
528 Items&: Precomp.PrecompFilePath);
529 return Error::success();
530}
531
532Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
533 EndPrecompRecord &EP) {
534 P.format(Fmt: " signature = {0:X+}", Items&: EP.Signature);
535 return Error::success();
536}
537
538Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
539 NestedTypeRecord &Nested) {
540 P.format(Fmt: " [name = `{0}`, parent = {1}]", Items&: Nested.Name, Items&: Nested.Type);
541 return Error::success();
542}
543
544Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
545 OneMethodRecord &Method) {
546 P.format(Fmt: " [name = `{0}`]", Items&: Method.Name);
547 AutoIndent Indent(P);
548 P.formatLine(Fmt: "type = {0}, vftable offset = {1}, attrs = {2}", Items&: Method.Type,
549 Items&: Method.VFTableOffset, Items: memberAttributes(Attrs: Method.Attrs));
550 return Error::success();
551}
552
553Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
554 OverloadedMethodRecord &Method) {
555 P.format(Fmt: " [name = `{0}`, # overloads = {1}, overload list = {2}]",
556 Items&: Method.Name, Items&: Method.NumOverloads, Items&: Method.MethodList);
557 return Error::success();
558}
559
560Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
561 DataMemberRecord &Field) {
562 P.format(Fmt: " [name = `{0}`, Type = {1}, offset = {2}, attrs = {3}]", Items&: Field.Name,
563 Items&: Field.Type, Items&: Field.FieldOffset, Items: memberAttributes(Attrs: Field.Attrs));
564 return Error::success();
565}
566
567Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
568 StaticDataMemberRecord &Field) {
569 P.format(Fmt: " [name = `{0}`, type = {1}, attrs = {2}]", Items&: Field.Name, Items&: Field.Type,
570 Items: memberAttributes(Attrs: Field.Attrs));
571 return Error::success();
572}
573
574Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
575 EnumeratorRecord &Enum) {
576 P.format(Fmt: " [{0} = {1}]", Items&: Enum.Name,
577 Items: toString(I: Enum.Value, Radix: 10, Signed: Enum.Value.isSigned()));
578 return Error::success();
579}
580
581Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
582 BaseClassRecord &Base) {
583 AutoIndent Indent(P);
584 P.formatLine(Fmt: "type = {0}, offset = {1}, attrs = {2}", Items&: Base.Type, Items&: Base.Offset,
585 Items: memberAttributes(Attrs: Base.Attrs));
586 return Error::success();
587}
588
589Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
590 VirtualBaseClassRecord &Base) {
591 AutoIndent Indent(P);
592 P.formatLine(
593 Fmt: "base = {0}, vbptr = {1}, vbptr offset = {2}, vtable index = {3}",
594 Items&: Base.BaseType, Items&: Base.VBPtrType, Items&: Base.VBPtrOffset, Items&: Base.VTableIndex);
595 P.formatLine(Fmt: "attrs = {0}", Items: memberAttributes(Attrs: Base.Attrs));
596 return Error::success();
597}
598
599Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
600 ListContinuationRecord &Cont) {
601 P.format(Fmt: " continuation = {0}", Items&: Cont.ContinuationIndex);
602 return Error::success();
603}
604
605Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
606 VFPtrRecord &VFP) {
607 P.format(Fmt: " type = {0}", Items&: VFP.Type);
608 return Error::success();
609}
610