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