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 P.Unindent(Amount: Width + 3);
265 if (RecordBytes) {
266 AutoIndent Indent(P, 9);
267 P.formatBinary(Label: "Bytes", Data: Record.RecordData, StartOffset: 0);
268 }
269 return Error::success();
270}
271
272Error MinimalTypeDumpVisitor::visitMemberBegin(CVMemberRecord &Record) {
273 P.formatLine(Fmt: "- {0}", Items: formatTypeLeafKind(K: Record.Kind));
274 return Error::success();
275}
276
277Error MinimalTypeDumpVisitor::visitMemberEnd(CVMemberRecord &Record) {
278 if (RecordBytes) {
279 AutoIndent Indent(P, 2);
280 P.formatBinary(Label: "Bytes", Data: Record.Data, StartOffset: 0);
281 }
282 return Error::success();
283}
284
285StringRef MinimalTypeDumpVisitor::getTypeName(TypeIndex TI) const {
286 if (TI.isNoneType())
287 return "";
288 return Types.getTypeName(Index: TI);
289}
290
291Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
292 FieldListRecord &FieldList) {
293 if (auto EC = codeview::visitMemberRecordStream(FieldList: FieldList.Data, Callbacks&: *this))
294 return EC;
295
296 return Error::success();
297}
298
299Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
300 StringIdRecord &String) {
301 P.format(Fmt: " ID: {0}, String: {1}", Items: String.getId(), Items: 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(Range&: Indices);
312 uint32_t W = NumDigits(N: Max->getIndex()) + 2;
313
314 for (auto I : Indices)
315 P.formatLine(Fmt: "{0}: `{1}`", Items: fmt_align(Item&: I, Where: AlignStyle::Right, Amount: W),
316 Items: getTypeName(TI: 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(Range&: Indices);
327 uint32_t W = NumDigits(N: Max->getIndex()) + 2;
328
329 for (auto I : Indices)
330 P.formatLine(Fmt: "{0}: `{1}`", Items: fmt_align(Item&: I, Where: AlignStyle::Right, Amount: W),
331 Items: getTypeName(TI: I));
332 return Error::success();
333}
334
335Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
336 ClassRecord &Class) {
337 P.format(Fmt: " `{0}`", Items&: Class.Name);
338 if (Class.hasUniqueName())
339 P.formatLine(Fmt: "unique name: `{0}`", Items&: Class.UniqueName);
340 P.formatLine(Fmt: "vtable: {0}, base list: {1}, field list: {2}",
341 Items&: Class.VTableShape, Items&: Class.DerivationList, Items&: Class.FieldList);
342 P.formatLine(Fmt: "options: {0}, sizeof {1}",
343 Items: formatClassOptions(IndentLevel: P.getIndentLevel(), Options: Class.Options, Stream,
344 CurrentTypeIndex),
345 Items&: Class.Size);
346 return Error::success();
347}
348
349Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
350 UnionRecord &Union) {
351 P.format(Fmt: " `{0}`", Items&: Union.Name);
352 if (Union.hasUniqueName())
353 P.formatLine(Fmt: "unique name: `{0}`", Items&: Union.UniqueName);
354 P.formatLine(Fmt: "field list: {0}", Items&: Union.FieldList);
355 P.formatLine(Fmt: "options: {0}, sizeof {1}",
356 Items: formatClassOptions(IndentLevel: P.getIndentLevel(), Options: Union.Options, Stream,
357 CurrentTypeIndex),
358 Items&: Union.Size);
359 return Error::success();
360}
361
362Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, EnumRecord &Enum) {
363 P.format(Fmt: " `{0}`", Items&: Enum.Name);
364 if (Enum.hasUniqueName())
365 P.formatLine(Fmt: "unique name: `{0}`", Items&: Enum.UniqueName);
366 P.formatLine(Fmt: "field list: {0}, underlying type: {1}", Items&: Enum.FieldList,
367 Items&: Enum.UnderlyingType);
368 P.formatLine(Fmt: "options: {0}",
369 Items: formatClassOptions(IndentLevel: P.getIndentLevel(), Options: 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(Fmt: "size: {0}, index type: {1}, element type: {2}", Items&: AT.Size,
377 Items&: AT.IndexType, Items&: AT.ElementType);
378 } else {
379 P.formatLine(Fmt: "name: {0}, size: {1}, index type: {2}, element type: {3}",
380 Items&: AT.Name, Items&: AT.Size, Items&: AT.IndexType, Items&: AT.ElementType);
381 }
382 return Error::success();
383}
384
385Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
386 VFTableRecord &VFT) {
387 P.formatLine(Fmt: "offset: {0}, complete class: {1}, overridden vftable: {2}",
388 Items&: VFT.VFPtrOffset, Items&: VFT.CompleteClass, Items&: VFT.OverriddenVFTable);
389 P.formatLine(Fmt: "method names: ");
390 if (!VFT.MethodNames.empty()) {
391 std::string Sep =
392 formatv(Fmt: "\n{0}",
393 Vals: fmt_repeat(Item: ' ', Count: P.getIndentLevel() + strlen(s: "method names: ")))
394 .str();
395 P.print(T: join(R&: VFT.MethodNames, Separator: Sep));
396 }
397 return Error::success();
398}
399
400Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
401 MemberFuncIdRecord &Id) {
402 P.formatLine(Fmt: "name = {0}, type = {1}, class type = {2}", Items&: Id.Name,
403 Items&: Id.FunctionType, Items&: Id.ClassType);
404 return Error::success();
405}
406
407Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
408 ProcedureRecord &Proc) {
409 P.formatLine(Fmt: "return type = {0}, # args = {1}, param list = {2}",
410 Items&: Proc.ReturnType, Items&: Proc.ParameterCount, Items&: Proc.ArgumentList);
411 P.formatLine(Fmt: "calling conv = {0}, options = {1}",
412 Items: formatCallingConvention(Convention: Proc.CallConv),
413 Items: formatFunctionOptions(Options: Proc.Options));
414 return Error::success();
415}
416
417Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
418 MemberFunctionRecord &MF) {
419 P.formatLine(Fmt: "return type = {0}, # args = {1}, param list = {2}",
420 Items&: MF.ReturnType, Items&: MF.ParameterCount, Items&: MF.ArgumentList);
421 P.formatLine(Fmt: "class type = {0}, this type = {1}, this adjust = {2}",
422 Items&: MF.ClassType, Items&: MF.ThisType, Items&: MF.ThisPointerAdjustment);
423 P.formatLine(Fmt: "calling conv = {0}, options = {1}",
424 Items: formatCallingConvention(Convention: MF.CallConv),
425 Items: formatFunctionOptions(Options: MF.Options));
426 return Error::success();
427}
428
429Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
430 FuncIdRecord &Func) {
431 P.formatLine(Fmt: "name = {0}, type = {1}, parent scope = {2}", Items&: Func.Name,
432 Items&: Func.FunctionType, Items&: Func.ParentScope);
433 return Error::success();
434}
435
436Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
437 TypeServer2Record &TS) {
438 P.formatLine(Fmt: "name = {0}, age = {1}, guid = {2}", Items&: TS.Name, Items&: TS.Age, Items&: TS.Guid);
439 return Error::success();
440}
441
442Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
443 PointerRecord &Ptr) {
444 P.formatLine(Fmt: "referent = {0}, {1}", Items&: Ptr.ReferentType,
445 Items: formatPointerAttrs(Record: Ptr));
446 return Error::success();
447}
448
449Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
450 ModifierRecord &Mod) {
451 P.formatLine(Fmt: "referent = {0}, modifiers = {1}", Items&: Mod.ModifiedType,
452 Items: modifierOptions(Options: 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(Fmt: "udt = {0}, mod = {1}, file = {2}, line = {3}", Items&: U.UDT, Items&: U.Module,
464 Items: U.SourceFile.getIndex(), Items&: U.LineNumber);
465 return Error::success();
466}
467
468Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
469 UdtSourceLineRecord &U) {
470 P.formatLine(Fmt: "udt = {0}, file = {1}, line = {2}", Items&: U.UDT,
471 Items: U.SourceFile.getIndex(), Items&: U.LineNumber);
472 return Error::success();
473}
474
475Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
476 BitFieldRecord &BF) {
477 P.formatLine(Fmt: "type = {0}, bit offset = {1}, # bits = {2}", Items&: BF.Type,
478 Items&: BF.BitOffset, Items&: 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(Fmt: "- Method [type = {0}, vftable offset = {1}, attrs = {2}]",
486 Items&: M.Type, Items&: M.VFTableOffset, Items: memberAttributes(Attrs: 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(Range&: Indices);
497 uint32_t W = NumDigits(N: Max->getIndex()) + 2;
498
499 for (auto I : Indices)
500 P.formatLine(Fmt: "{0}: `{1}`", Items: fmt_align(Item&: I, Where: AlignStyle::Right, Amount: W),
501 Items: getTypeName(TI: 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(Fmt: " type = {0}", Items&: Type);
508 return Error::success();
509}
510
511Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
512 PrecompRecord &Precomp) {
513 P.format(Fmt: " start index = {0:X+}, types count = {1:X+}, signature = {2:X+},"
514 " precomp path = {3}",
515 Items&: Precomp.StartTypeIndex, Items&: Precomp.TypesCount, Items&: Precomp.Signature,
516 Items&: Precomp.PrecompFilePath);
517 return Error::success();
518}
519
520Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
521 EndPrecompRecord &EP) {
522 P.format(Fmt: " signature = {0:X+}", Items&: EP.Signature);
523 return Error::success();
524}
525
526Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
527 NestedTypeRecord &Nested) {
528 P.format(Fmt: " [name = `{0}`, parent = {1}]", Items&: Nested.Name, Items&: Nested.Type);
529 return Error::success();
530}
531
532Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
533 OneMethodRecord &Method) {
534 P.format(Fmt: " [name = `{0}`]", Items&: Method.Name);
535 AutoIndent Indent(P);
536 P.formatLine(Fmt: "type = {0}, vftable offset = {1}, attrs = {2}", Items&: Method.Type,
537 Items&: Method.VFTableOffset, Items: memberAttributes(Attrs: Method.Attrs));
538 return Error::success();
539}
540
541Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
542 OverloadedMethodRecord &Method) {
543 P.format(Fmt: " [name = `{0}`, # overloads = {1}, overload list = {2}]",
544 Items&: Method.Name, Items&: Method.NumOverloads, Items&: Method.MethodList);
545 return Error::success();
546}
547
548Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
549 DataMemberRecord &Field) {
550 P.format(Fmt: " [name = `{0}`, Type = {1}, offset = {2}, attrs = {3}]", Items&: Field.Name,
551 Items&: Field.Type, Items&: Field.FieldOffset, Items: memberAttributes(Attrs: Field.Attrs));
552 return Error::success();
553}
554
555Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
556 StaticDataMemberRecord &Field) {
557 P.format(Fmt: " [name = `{0}`, type = {1}, attrs = {2}]", Items&: Field.Name, Items&: Field.Type,
558 Items: memberAttributes(Attrs: Field.Attrs));
559 return Error::success();
560}
561
562Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
563 EnumeratorRecord &Enum) {
564 P.format(Fmt: " [{0} = {1}]", Items&: Enum.Name,
565 Items: toString(I: Enum.Value, Radix: 10, Signed: Enum.Value.isSigned()));
566 return Error::success();
567}
568
569Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
570 BaseClassRecord &Base) {
571 AutoIndent Indent(P);
572 P.formatLine(Fmt: "type = {0}, offset = {1}, attrs = {2}", Items&: Base.Type, Items&: Base.Offset,
573 Items: memberAttributes(Attrs: Base.Attrs));
574 return Error::success();
575}
576
577Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
578 VirtualBaseClassRecord &Base) {
579 AutoIndent Indent(P);
580 P.formatLine(
581 Fmt: "base = {0}, vbptr = {1}, vbptr offset = {2}, vtable index = {3}",
582 Items&: Base.BaseType, Items&: Base.VBPtrType, Items&: Base.VBPtrOffset, Items&: Base.VTableIndex);
583 P.formatLine(Fmt: "attrs = {0}", Items: memberAttributes(Attrs: Base.Attrs));
584 return Error::success();
585}
586
587Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
588 ListContinuationRecord &Cont) {
589 P.format(Fmt: " continuation = {0}", Items&: Cont.ContinuationIndex);
590 return Error::success();
591}
592
593Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
594 VFPtrRecord &VFP) {
595 P.format(Fmt: " type = {0}", Items&: VFP.Type);
596 return Error::success();
597}
598