xref: /freebsd/contrib/llvm-project/llvm/lib/Demangle/MicrosoftDemangleNodes.cpp (revision 1719886f6d08408b834d270c59ffcfd821c8f63a)
1 //===- MicrosoftDemangle.cpp ----------------------------------------------===//
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 // This file defines a demangler for MSVC-style mangled symbols.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "llvm/Demangle/MicrosoftDemangleNodes.h"
14 #include "llvm/Demangle/Utility.h"
15 #include <cctype>
16 #include <string>
17 
18 using namespace llvm;
19 using namespace ms_demangle;
20 
21 #define OUTPUT_ENUM_CLASS_VALUE(Enum, Value, Desc)                             \
22   case Enum::Value:                                                            \
23     OB << Desc;                                                                \
24     break;
25 
26 // Writes a space if the last token does not end with a punctuation.
27 static void outputSpaceIfNecessary(OutputBuffer &OB) {
28   if (OB.empty())
29     return;
30 
31   char C = OB.back();
32   if (std::isalnum(C) || C == '>')
33     OB << " ";
34 }
35 
36 static void outputSingleQualifier(OutputBuffer &OB, Qualifiers Q) {
37   switch (Q) {
38   case Q_Const:
39     OB << "const";
40     break;
41   case Q_Volatile:
42     OB << "volatile";
43     break;
44   case Q_Restrict:
45     OB << "__restrict";
46     break;
47   default:
48     break;
49   }
50 }
51 
52 static bool outputQualifierIfPresent(OutputBuffer &OB, Qualifiers Q,
53                                      Qualifiers Mask, bool NeedSpace) {
54   if (!(Q & Mask))
55     return NeedSpace;
56 
57   if (NeedSpace)
58     OB << " ";
59 
60   outputSingleQualifier(OB, Mask);
61   return true;
62 }
63 
64 static void outputQualifiers(OutputBuffer &OB, Qualifiers Q, bool SpaceBefore,
65                              bool SpaceAfter) {
66   if (Q == Q_None)
67     return;
68 
69   size_t Pos1 = OB.getCurrentPosition();
70   SpaceBefore = outputQualifierIfPresent(OB, Q, Q_Const, SpaceBefore);
71   SpaceBefore = outputQualifierIfPresent(OB, Q, Q_Volatile, SpaceBefore);
72   SpaceBefore = outputQualifierIfPresent(OB, Q, Q_Restrict, SpaceBefore);
73   size_t Pos2 = OB.getCurrentPosition();
74   if (SpaceAfter && Pos2 > Pos1)
75     OB << " ";
76 }
77 
78 static void outputCallingConvention(OutputBuffer &OB, CallingConv CC) {
79   outputSpaceIfNecessary(OB);
80 
81   switch (CC) {
82   case CallingConv::Cdecl:
83     OB << "__cdecl";
84     break;
85   case CallingConv::Fastcall:
86     OB << "__fastcall";
87     break;
88   case CallingConv::Pascal:
89     OB << "__pascal";
90     break;
91   case CallingConv::Regcall:
92     OB << "__regcall";
93     break;
94   case CallingConv::Stdcall:
95     OB << "__stdcall";
96     break;
97   case CallingConv::Thiscall:
98     OB << "__thiscall";
99     break;
100   case CallingConv::Eabi:
101     OB << "__eabi";
102     break;
103   case CallingConv::Vectorcall:
104     OB << "__vectorcall";
105     break;
106   case CallingConv::Clrcall:
107     OB << "__clrcall";
108     break;
109   case CallingConv::Swift:
110     OB << "__attribute__((__swiftcall__)) ";
111     break;
112   case CallingConv::SwiftAsync:
113     OB << "__attribute__((__swiftasynccall__)) ";
114     break;
115   default:
116     break;
117   }
118 }
119 
120 std::string Node::toString(OutputFlags Flags) const {
121   OutputBuffer OB;
122   this->output(OB, Flags);
123   std::string_view SV = OB;
124   std::string Owned(SV.begin(), SV.end());
125   std::free(OB.getBuffer());
126   return Owned;
127 }
128 
129 void PrimitiveTypeNode::outputPre(OutputBuffer &OB, OutputFlags Flags) const {
130   switch (PrimKind) {
131     OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Void, "void");
132     OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Bool, "bool");
133     OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Char, "char");
134     OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Schar, "signed char");
135     OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Uchar, "unsigned char");
136     OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Char8, "char8_t");
137     OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Char16, "char16_t");
138     OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Char32, "char32_t");
139     OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Short, "short");
140     OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Ushort, "unsigned short");
141     OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Int, "int");
142     OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Uint, "unsigned int");
143     OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Long, "long");
144     OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Ulong, "unsigned long");
145     OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Int64, "__int64");
146     OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Uint64, "unsigned __int64");
147     OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Wchar, "wchar_t");
148     OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Float, "float");
149     OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Double, "double");
150     OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Ldouble, "long double");
151     OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Nullptr, "std::nullptr_t");
152   }
153   outputQualifiers(OB, Quals, true, false);
154 }
155 
156 void NodeArrayNode::output(OutputBuffer &OB, OutputFlags Flags) const {
157   output(OB, Flags, ", ");
158 }
159 
160 void NodeArrayNode::output(OutputBuffer &OB, OutputFlags Flags,
161                            std::string_view Separator) const {
162   if (Count == 0)
163     return;
164   if (Nodes[0])
165     Nodes[0]->output(OB, Flags);
166   for (size_t I = 1; I < Count; ++I) {
167     OB << Separator;
168     Nodes[I]->output(OB, Flags);
169   }
170 }
171 
172 void EncodedStringLiteralNode::output(OutputBuffer &OB,
173                                       OutputFlags Flags) const {
174   switch (Char) {
175   case CharKind::Wchar:
176     OB << "L\"";
177     break;
178   case CharKind::Char:
179     OB << "\"";
180     break;
181   case CharKind::Char16:
182     OB << "u\"";
183     break;
184   case CharKind::Char32:
185     OB << "U\"";
186     break;
187   }
188   OB << DecodedString << "\"";
189   if (IsTruncated)
190     OB << "...";
191 }
192 
193 void IntegerLiteralNode::output(OutputBuffer &OB, OutputFlags Flags) const {
194   if (IsNegative)
195     OB << '-';
196   OB << Value;
197 }
198 
199 void TemplateParameterReferenceNode::output(OutputBuffer &OB,
200                                             OutputFlags Flags) const {
201   if (ThunkOffsetCount > 0)
202     OB << "{";
203   else if (Affinity == PointerAffinity::Pointer)
204     OB << "&";
205 
206   if (Symbol) {
207     Symbol->output(OB, Flags);
208     if (ThunkOffsetCount > 0)
209       OB << ", ";
210   }
211 
212   if (ThunkOffsetCount > 0)
213     OB << ThunkOffsets[0];
214   for (int I = 1; I < ThunkOffsetCount; ++I) {
215     OB << ", " << ThunkOffsets[I];
216   }
217   if (ThunkOffsetCount > 0)
218     OB << "}";
219 }
220 
221 void IdentifierNode::outputTemplateParameters(OutputBuffer &OB,
222                                               OutputFlags Flags) const {
223   if (!TemplateParams)
224     return;
225   OB << "<";
226   TemplateParams->output(OB, Flags);
227   OB << ">";
228 }
229 
230 void DynamicStructorIdentifierNode::output(OutputBuffer &OB,
231                                            OutputFlags Flags) const {
232   if (IsDestructor)
233     OB << "`dynamic atexit destructor for ";
234   else
235     OB << "`dynamic initializer for ";
236 
237   if (Variable) {
238     OB << "`";
239     Variable->output(OB, Flags);
240     OB << "''";
241   } else {
242     OB << "'";
243     Name->output(OB, Flags);
244     OB << "''";
245   }
246 }
247 
248 void NamedIdentifierNode::output(OutputBuffer &OB, OutputFlags Flags) const {
249   OB << Name;
250   outputTemplateParameters(OB, Flags);
251 }
252 
253 void IntrinsicFunctionIdentifierNode::output(OutputBuffer &OB,
254                                              OutputFlags Flags) const {
255   switch (Operator) {
256     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, New, "operator new");
257     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Delete, "operator delete");
258     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Assign, "operator=");
259     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, RightShift, "operator>>");
260     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LeftShift, "operator<<");
261     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LogicalNot, "operator!");
262     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Equals, "operator==");
263     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, NotEquals, "operator!=");
264     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ArraySubscript,
265                             "operator[]");
266     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Pointer, "operator->");
267     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Increment, "operator++");
268     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Decrement, "operator--");
269     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Minus, "operator-");
270     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Plus, "operator+");
271     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Dereference, "operator*");
272     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, BitwiseAnd, "operator&");
273     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, MemberPointer,
274                             "operator->*");
275     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Divide, "operator/");
276     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Modulus, "operator%");
277     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LessThan, "operator<");
278     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LessThanEqual, "operator<=");
279     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, GreaterThan, "operator>");
280     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, GreaterThanEqual,
281                             "operator>=");
282     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Comma, "operator,");
283     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Parens, "operator()");
284     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, BitwiseNot, "operator~");
285     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, BitwiseXor, "operator^");
286     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, BitwiseOr, "operator|");
287     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LogicalAnd, "operator&&");
288     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LogicalOr, "operator||");
289     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, TimesEqual, "operator*=");
290     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, PlusEqual, "operator+=");
291     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, MinusEqual, "operator-=");
292     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, DivEqual, "operator/=");
293     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ModEqual, "operator%=");
294     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, RshEqual, "operator>>=");
295     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LshEqual, "operator<<=");
296     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, BitwiseAndEqual,
297                             "operator&=");
298     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, BitwiseOrEqual,
299                             "operator|=");
300     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, BitwiseXorEqual,
301                             "operator^=");
302     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VbaseDtor, "`vbase dtor'");
303     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VecDelDtor,
304                             "`vector deleting dtor'");
305     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, DefaultCtorClosure,
306                             "`default ctor closure'");
307     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ScalarDelDtor,
308                             "`scalar deleting dtor'");
309     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VecCtorIter,
310                             "`vector ctor iterator'");
311     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VecDtorIter,
312                             "`vector dtor iterator'");
313     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VecVbaseCtorIter,
314                             "`vector vbase ctor iterator'");
315     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VdispMap,
316                             "`virtual displacement map'");
317     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, EHVecCtorIter,
318                             "`eh vector ctor iterator'");
319     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, EHVecDtorIter,
320                             "`eh vector dtor iterator'");
321     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, EHVecVbaseCtorIter,
322                             "`eh vector vbase ctor iterator'");
323     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, CopyCtorClosure,
324                             "`copy ctor closure'");
325     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LocalVftableCtorClosure,
326                             "`local vftable ctor closure'");
327     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ArrayNew, "operator new[]");
328     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ArrayDelete,
329                             "operator delete[]");
330     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ManVectorCtorIter,
331                             "`managed vector ctor iterator'");
332     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ManVectorDtorIter,
333                             "`managed vector dtor iterator'");
334     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, EHVectorCopyCtorIter,
335                             "`EH vector copy ctor iterator'");
336     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, EHVectorVbaseCopyCtorIter,
337                             "`EH vector vbase copy ctor iterator'");
338     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VectorCopyCtorIter,
339                             "`vector copy ctor iterator'");
340     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VectorVbaseCopyCtorIter,
341                             "`vector vbase copy constructor iterator'");
342     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ManVectorVbaseCopyCtorIter,
343                             "`managed vector vbase copy constructor iterator'");
344     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, CoAwait,
345                             "operator co_await");
346     OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Spaceship, "operator<=>");
347   case IntrinsicFunctionKind::MaxIntrinsic:
348   case IntrinsicFunctionKind::None:
349     break;
350   }
351   outputTemplateParameters(OB, Flags);
352 }
353 
354 void LocalStaticGuardIdentifierNode::output(OutputBuffer &OB,
355                                             OutputFlags Flags) const {
356   if (IsThread)
357     OB << "`local static thread guard'";
358   else
359     OB << "`local static guard'";
360   if (ScopeIndex > 0)
361     OB << "{" << ScopeIndex << "}";
362 }
363 
364 void ConversionOperatorIdentifierNode::output(OutputBuffer &OB,
365                                               OutputFlags Flags) const {
366   OB << "operator";
367   outputTemplateParameters(OB, Flags);
368   OB << " ";
369   TargetType->output(OB, Flags);
370 }
371 
372 void StructorIdentifierNode::output(OutputBuffer &OB, OutputFlags Flags) const {
373   if (IsDestructor)
374     OB << "~";
375   Class->output(OB, Flags);
376   outputTemplateParameters(OB, Flags);
377 }
378 
379 void LiteralOperatorIdentifierNode::output(OutputBuffer &OB,
380                                            OutputFlags Flags) const {
381   OB << "operator \"\"" << Name;
382   outputTemplateParameters(OB, Flags);
383 }
384 
385 void FunctionSignatureNode::outputPre(OutputBuffer &OB,
386                                       OutputFlags Flags) const {
387   if (!(Flags & OF_NoAccessSpecifier)) {
388     if (FunctionClass & FC_Public)
389       OB << "public: ";
390     if (FunctionClass & FC_Protected)
391       OB << "protected: ";
392     if (FunctionClass & FC_Private)
393       OB << "private: ";
394   }
395 
396   if (!(Flags & OF_NoMemberType)) {
397     if (!(FunctionClass & FC_Global)) {
398       if (FunctionClass & FC_Static)
399         OB << "static ";
400     }
401     if (FunctionClass & FC_Virtual)
402       OB << "virtual ";
403 
404     if (FunctionClass & FC_ExternC)
405       OB << "extern \"C\" ";
406   }
407 
408   if (!(Flags & OF_NoReturnType) && ReturnType) {
409     ReturnType->outputPre(OB, Flags);
410     OB << " ";
411   }
412 
413   if (!(Flags & OF_NoCallingConvention))
414     outputCallingConvention(OB, CallConvention);
415 }
416 
417 void FunctionSignatureNode::outputPost(OutputBuffer &OB,
418                                        OutputFlags Flags) const {
419   if (!(FunctionClass & FC_NoParameterList)) {
420     OB << "(";
421     if (Params)
422       Params->output(OB, Flags);
423     else
424       OB << "void";
425 
426     if (IsVariadic) {
427       if (OB.back() != '(')
428         OB << ", ";
429       OB << "...";
430     }
431     OB << ")";
432   }
433 
434   if (Quals & Q_Const)
435     OB << " const";
436   if (Quals & Q_Volatile)
437     OB << " volatile";
438   if (Quals & Q_Restrict)
439     OB << " __restrict";
440   if (Quals & Q_Unaligned)
441     OB << " __unaligned";
442 
443   if (IsNoexcept)
444     OB << " noexcept";
445 
446   if (RefQualifier == FunctionRefQualifier::Reference)
447     OB << " &";
448   else if (RefQualifier == FunctionRefQualifier::RValueReference)
449     OB << " &&";
450 
451   if (!(Flags & OF_NoReturnType) && ReturnType)
452     ReturnType->outputPost(OB, Flags);
453 }
454 
455 void ThunkSignatureNode::outputPre(OutputBuffer &OB, OutputFlags Flags) const {
456   OB << "[thunk]: ";
457 
458   FunctionSignatureNode::outputPre(OB, Flags);
459 }
460 
461 void ThunkSignatureNode::outputPost(OutputBuffer &OB, OutputFlags Flags) const {
462   if (FunctionClass & FC_StaticThisAdjust) {
463     OB << "`adjustor{" << ThisAdjust.StaticOffset << "}'";
464   } else if (FunctionClass & FC_VirtualThisAdjust) {
465     if (FunctionClass & FC_VirtualThisAdjustEx) {
466       OB << "`vtordispex{" << ThisAdjust.VBPtrOffset << ", "
467          << ThisAdjust.VBOffsetOffset << ", " << ThisAdjust.VtordispOffset
468          << ", " << ThisAdjust.StaticOffset << "}'";
469     } else {
470       OB << "`vtordisp{" << ThisAdjust.VtordispOffset << ", "
471          << ThisAdjust.StaticOffset << "}'";
472     }
473   }
474 
475   FunctionSignatureNode::outputPost(OB, Flags);
476 }
477 
478 void PointerTypeNode::outputPre(OutputBuffer &OB, OutputFlags Flags) const {
479   if (Pointee->kind() == NodeKind::FunctionSignature) {
480     // If this is a pointer to a function, don't output the calling convention.
481     // It needs to go inside the parentheses.
482     const FunctionSignatureNode *Sig =
483         static_cast<const FunctionSignatureNode *>(Pointee);
484     Sig->outputPre(OB, OF_NoCallingConvention);
485   } else
486     Pointee->outputPre(OB, Flags);
487 
488   outputSpaceIfNecessary(OB);
489 
490   if (Quals & Q_Unaligned)
491     OB << "__unaligned ";
492 
493   if (Pointee->kind() == NodeKind::ArrayType) {
494     OB << "(";
495   } else if (Pointee->kind() == NodeKind::FunctionSignature) {
496     OB << "(";
497     const FunctionSignatureNode *Sig =
498         static_cast<const FunctionSignatureNode *>(Pointee);
499     outputCallingConvention(OB, Sig->CallConvention);
500     OB << " ";
501   }
502 
503   if (ClassParent) {
504     ClassParent->output(OB, Flags);
505     OB << "::";
506   }
507 
508   switch (Affinity) {
509   case PointerAffinity::Pointer:
510     OB << "*";
511     break;
512   case PointerAffinity::Reference:
513     OB << "&";
514     break;
515   case PointerAffinity::RValueReference:
516     OB << "&&";
517     break;
518   default:
519     assert(false);
520   }
521   outputQualifiers(OB, Quals, false, false);
522 }
523 
524 void PointerTypeNode::outputPost(OutputBuffer &OB, OutputFlags Flags) const {
525   if (Pointee->kind() == NodeKind::ArrayType ||
526       Pointee->kind() == NodeKind::FunctionSignature)
527     OB << ")";
528 
529   Pointee->outputPost(OB, Flags);
530 }
531 
532 void TagTypeNode::outputPre(OutputBuffer &OB, OutputFlags Flags) const {
533   if (!(Flags & OF_NoTagSpecifier)) {
534     switch (Tag) {
535       OUTPUT_ENUM_CLASS_VALUE(TagKind, Class, "class");
536       OUTPUT_ENUM_CLASS_VALUE(TagKind, Struct, "struct");
537       OUTPUT_ENUM_CLASS_VALUE(TagKind, Union, "union");
538       OUTPUT_ENUM_CLASS_VALUE(TagKind, Enum, "enum");
539     }
540     OB << " ";
541   }
542   QualifiedName->output(OB, Flags);
543   outputQualifiers(OB, Quals, true, false);
544 }
545 
546 void TagTypeNode::outputPost(OutputBuffer &OB, OutputFlags Flags) const {}
547 
548 void ArrayTypeNode::outputPre(OutputBuffer &OB, OutputFlags Flags) const {
549   ElementType->outputPre(OB, Flags);
550   outputQualifiers(OB, Quals, true, false);
551 }
552 
553 void ArrayTypeNode::outputOneDimension(OutputBuffer &OB, OutputFlags Flags,
554                                        Node *N) const {
555   assert(N->kind() == NodeKind::IntegerLiteral);
556   IntegerLiteralNode *ILN = static_cast<IntegerLiteralNode *>(N);
557   if (ILN->Value != 0)
558     ILN->output(OB, Flags);
559 }
560 
561 void ArrayTypeNode::outputDimensionsImpl(OutputBuffer &OB,
562                                          OutputFlags Flags) const {
563   if (Dimensions->Count == 0)
564     return;
565 
566   outputOneDimension(OB, Flags, Dimensions->Nodes[0]);
567   for (size_t I = 1; I < Dimensions->Count; ++I) {
568     OB << "][";
569     outputOneDimension(OB, Flags, Dimensions->Nodes[I]);
570   }
571 }
572 
573 void ArrayTypeNode::outputPost(OutputBuffer &OB, OutputFlags Flags) const {
574   OB << "[";
575   outputDimensionsImpl(OB, Flags);
576   OB << "]";
577 
578   ElementType->outputPost(OB, Flags);
579 }
580 
581 void SymbolNode::output(OutputBuffer &OB, OutputFlags Flags) const {
582   Name->output(OB, Flags);
583 }
584 
585 void FunctionSymbolNode::output(OutputBuffer &OB, OutputFlags Flags) const {
586   Signature->outputPre(OB, Flags);
587   outputSpaceIfNecessary(OB);
588   Name->output(OB, Flags);
589   Signature->outputPost(OB, Flags);
590 }
591 
592 void VariableSymbolNode::output(OutputBuffer &OB, OutputFlags Flags) const {
593   const char *AccessSpec = nullptr;
594   bool IsStatic = true;
595   switch (SC) {
596   case StorageClass::PrivateStatic:
597     AccessSpec = "private";
598     break;
599   case StorageClass::PublicStatic:
600     AccessSpec = "public";
601     break;
602   case StorageClass::ProtectedStatic:
603     AccessSpec = "protected";
604     break;
605   default:
606     IsStatic = false;
607     break;
608   }
609   if (!(Flags & OF_NoAccessSpecifier) && AccessSpec)
610     OB << AccessSpec << ": ";
611   if (!(Flags & OF_NoMemberType) && IsStatic)
612     OB << "static ";
613 
614   if (!(Flags & OF_NoVariableType) && Type) {
615     Type->outputPre(OB, Flags);
616     outputSpaceIfNecessary(OB);
617   }
618   Name->output(OB, Flags);
619   if (!(Flags & OF_NoVariableType) && Type)
620     Type->outputPost(OB, Flags);
621 }
622 
623 void CustomTypeNode::outputPre(OutputBuffer &OB, OutputFlags Flags) const {
624   Identifier->output(OB, Flags);
625 }
626 void CustomTypeNode::outputPost(OutputBuffer &OB, OutputFlags Flags) const {}
627 
628 void QualifiedNameNode::output(OutputBuffer &OB, OutputFlags Flags) const {
629   Components->output(OB, Flags, "::");
630 }
631 
632 void RttiBaseClassDescriptorNode::output(OutputBuffer &OB,
633                                          OutputFlags Flags) const {
634   OB << "`RTTI Base Class Descriptor at (";
635   OB << NVOffset << ", " << VBPtrOffset << ", " << VBTableOffset << ", "
636      << this->Flags;
637   OB << ")'";
638 }
639 
640 void LocalStaticGuardVariableNode::output(OutputBuffer &OB,
641                                           OutputFlags Flags) const {
642   Name->output(OB, Flags);
643 }
644 
645 void VcallThunkIdentifierNode::output(OutputBuffer &OB,
646                                       OutputFlags Flags) const {
647   OB << "`vcall'{" << OffsetInVTable << ", {flat}}";
648 }
649 
650 void SpecialTableSymbolNode::output(OutputBuffer &OB, OutputFlags Flags) const {
651   outputQualifiers(OB, Quals, false, true);
652   Name->output(OB, Flags);
653   if (TargetName) {
654     OB << "{for `";
655     TargetName->output(OB, Flags);
656     OB << "'}";
657   }
658 }
659