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