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