//===- MicrosoftDemangle.cpp ----------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file defines a demangler for MSVC-style mangled symbols. // //===----------------------------------------------------------------------===// #include "llvm/Demangle/MicrosoftDemangleNodes.h" #include "llvm/Demangle/DemangleConfig.h" #include "llvm/Demangle/Utility.h" #include #include using namespace llvm; using namespace ms_demangle; #define OUTPUT_ENUM_CLASS_VALUE(Enum, Value, Desc) \ case Enum::Value: \ OS << Desc; \ break; // Writes a space if the last token does not end with a punctuation. static void outputSpaceIfNecessary(OutputStream &OS) { if (OS.empty()) return; char C = OS.back(); if (std::isalnum(C) || C == '>') OS << " "; } static void outputSingleQualifier(OutputStream &OS, Qualifiers Q) { switch (Q) { case Q_Const: OS << "const"; break; case Q_Volatile: OS << "volatile"; break; case Q_Restrict: OS << "__restrict"; break; default: break; } } static bool outputQualifierIfPresent(OutputStream &OS, Qualifiers Q, Qualifiers Mask, bool NeedSpace) { if (!(Q & Mask)) return NeedSpace; if (NeedSpace) OS << " "; outputSingleQualifier(OS, Mask); return true; } static void outputQualifiers(OutputStream &OS, Qualifiers Q, bool SpaceBefore, bool SpaceAfter) { if (Q == Q_None) return; size_t Pos1 = OS.getCurrentPosition(); SpaceBefore = outputQualifierIfPresent(OS, Q, Q_Const, SpaceBefore); SpaceBefore = outputQualifierIfPresent(OS, Q, Q_Volatile, SpaceBefore); SpaceBefore = outputQualifierIfPresent(OS, Q, Q_Restrict, SpaceBefore); size_t Pos2 = OS.getCurrentPosition(); if (SpaceAfter && Pos2 > Pos1) OS << " "; } static void outputCallingConvention(OutputStream &OS, CallingConv CC) { outputSpaceIfNecessary(OS); switch (CC) { case CallingConv::Cdecl: OS << "__cdecl"; break; case CallingConv::Fastcall: OS << "__fastcall"; break; case CallingConv::Pascal: OS << "__pascal"; break; case CallingConv::Regcall: OS << "__regcall"; break; case CallingConv::Stdcall: OS << "__stdcall"; break; case CallingConv::Thiscall: OS << "__thiscall"; break; case CallingConv::Eabi: OS << "__eabi"; break; case CallingConv::Vectorcall: OS << "__vectorcall"; break; case CallingConv::Clrcall: OS << "__clrcall"; break; case CallingConv::Swift: OS << "__attribute__((__swiftcall__)) "; break; case CallingConv::SwiftAsync: OS << "__attribute__((__swiftasynccall__)) "; break; default: break; } } std::string Node::toString(OutputFlags Flags) const { OutputStream OS; initializeOutputStream(nullptr, nullptr, OS, 1024); this->output(OS, Flags); OS << '\0'; std::string Owned(OS.getBuffer()); std::free(OS.getBuffer()); return Owned; } void PrimitiveTypeNode::outputPre(OutputStream &OS, OutputFlags Flags) const { switch (PrimKind) { OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Void, "void"); OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Bool, "bool"); OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Char, "char"); OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Schar, "signed char"); OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Uchar, "unsigned char"); OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Char8, "char8_t"); OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Char16, "char16_t"); OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Char32, "char32_t"); OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Short, "short"); OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Ushort, "unsigned short"); OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Int, "int"); OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Uint, "unsigned int"); OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Long, "long"); OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Ulong, "unsigned long"); OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Int64, "__int64"); OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Uint64, "unsigned __int64"); OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Wchar, "wchar_t"); OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Float, "float"); OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Double, "double"); OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Ldouble, "long double"); OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Nullptr, "std::nullptr_t"); } outputQualifiers(OS, Quals, true, false); } void NodeArrayNode::output(OutputStream &OS, OutputFlags Flags) const { output(OS, Flags, ", "); } void NodeArrayNode::output(OutputStream &OS, OutputFlags Flags, StringView Separator) const { if (Count == 0) return; if (Nodes[0]) Nodes[0]->output(OS, Flags); for (size_t I = 1; I < Count; ++I) { OS << Separator; Nodes[I]->output(OS, Flags); } } void EncodedStringLiteralNode::output(OutputStream &OS, OutputFlags Flags) const { switch (Char) { case CharKind::Wchar: OS << "L\""; break; case CharKind::Char: OS << "\""; break; case CharKind::Char16: OS << "u\""; break; case CharKind::Char32: OS << "U\""; break; } OS << DecodedString << "\""; if (IsTruncated) OS << "..."; } void IntegerLiteralNode::output(OutputStream &OS, OutputFlags Flags) const { if (IsNegative) OS << '-'; OS << Value; } void TemplateParameterReferenceNode::output(OutputStream &OS, OutputFlags Flags) const { if (ThunkOffsetCount > 0) OS << "{"; else if (Affinity == PointerAffinity::Pointer) OS << "&"; if (Symbol) { Symbol->output(OS, Flags); if (ThunkOffsetCount > 0) OS << ", "; } if (ThunkOffsetCount > 0) OS << ThunkOffsets[0]; for (int I = 1; I < ThunkOffsetCount; ++I) { OS << ", " << ThunkOffsets[I]; } if (ThunkOffsetCount > 0) OS << "}"; } void IdentifierNode::outputTemplateParameters(OutputStream &OS, OutputFlags Flags) const { if (!TemplateParams) return; OS << "<"; TemplateParams->output(OS, Flags); OS << ">"; } void DynamicStructorIdentifierNode::output(OutputStream &OS, OutputFlags Flags) const { if (IsDestructor) OS << "`dynamic atexit destructor for "; else OS << "`dynamic initializer for "; if (Variable) { OS << "`"; Variable->output(OS, Flags); OS << "''"; } else { OS << "'"; Name->output(OS, Flags); OS << "''"; } } void NamedIdentifierNode::output(OutputStream &OS, OutputFlags Flags) const { OS << Name; outputTemplateParameters(OS, Flags); } void IntrinsicFunctionIdentifierNode::output(OutputStream &OS, OutputFlags Flags) const { switch (Operator) { OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, New, "operator new"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Delete, "operator delete"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Assign, "operator="); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, RightShift, "operator>>"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LeftShift, "operator<<"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LogicalNot, "operator!"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Equals, "operator=="); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, NotEquals, "operator!="); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ArraySubscript, "operator[]"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Pointer, "operator->"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Increment, "operator++"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Decrement, "operator--"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Minus, "operator-"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Plus, "operator+"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Dereference, "operator*"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, BitwiseAnd, "operator&"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, MemberPointer, "operator->*"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Divide, "operator/"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Modulus, "operator%"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LessThan, "operator<"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LessThanEqual, "operator<="); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, GreaterThan, "operator>"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, GreaterThanEqual, "operator>="); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Comma, "operator,"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Parens, "operator()"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, BitwiseNot, "operator~"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, BitwiseXor, "operator^"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, BitwiseOr, "operator|"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LogicalAnd, "operator&&"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LogicalOr, "operator||"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, TimesEqual, "operator*="); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, PlusEqual, "operator+="); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, MinusEqual, "operator-="); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, DivEqual, "operator/="); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ModEqual, "operator%="); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, RshEqual, "operator>>="); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LshEqual, "operator<<="); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, BitwiseAndEqual, "operator&="); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, BitwiseOrEqual, "operator|="); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, BitwiseXorEqual, "operator^="); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VbaseDtor, "`vbase dtor'"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VecDelDtor, "`vector deleting dtor'"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, DefaultCtorClosure, "`default ctor closure'"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ScalarDelDtor, "`scalar deleting dtor'"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VecCtorIter, "`vector ctor iterator'"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VecDtorIter, "`vector dtor iterator'"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VecVbaseCtorIter, "`vector vbase ctor iterator'"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VdispMap, "`virtual displacement map'"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, EHVecCtorIter, "`eh vector ctor iterator'"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, EHVecDtorIter, "`eh vector dtor iterator'"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, EHVecVbaseCtorIter, "`eh vector vbase ctor iterator'"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, CopyCtorClosure, "`copy ctor closure'"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LocalVftableCtorClosure, "`local vftable ctor closure'"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ArrayNew, "operator new[]"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ArrayDelete, "operator delete[]"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ManVectorCtorIter, "`managed vector ctor iterator'"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ManVectorDtorIter, "`managed vector dtor iterator'"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, EHVectorCopyCtorIter, "`EH vector copy ctor iterator'"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, EHVectorVbaseCopyCtorIter, "`EH vector vbase copy ctor iterator'"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VectorCopyCtorIter, "`vector copy ctor iterator'"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VectorVbaseCopyCtorIter, "`vector vbase copy constructor iterator'"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ManVectorVbaseCopyCtorIter, "`managed vector vbase copy constructor iterator'"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, CoAwait, "operator co_await"); OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Spaceship, "operator<=>"); case IntrinsicFunctionKind::MaxIntrinsic: case IntrinsicFunctionKind::None: break; } outputTemplateParameters(OS, Flags); } void LocalStaticGuardIdentifierNode::output(OutputStream &OS, OutputFlags Flags) const { if (IsThread) OS << "`local static thread guard'"; else OS << "`local static guard'"; if (ScopeIndex > 0) OS << "{" << ScopeIndex << "}"; } void ConversionOperatorIdentifierNode::output(OutputStream &OS, OutputFlags Flags) const { OS << "operator"; outputTemplateParameters(OS, Flags); OS << " "; TargetType->output(OS, Flags); } void StructorIdentifierNode::output(OutputStream &OS, OutputFlags Flags) const { if (IsDestructor) OS << "~"; Class->output(OS, Flags); outputTemplateParameters(OS, Flags); } void LiteralOperatorIdentifierNode::output(OutputStream &OS, OutputFlags Flags) const { OS << "operator \"\"" << Name; outputTemplateParameters(OS, Flags); } void FunctionSignatureNode::outputPre(OutputStream &OS, OutputFlags Flags) const { if (!(Flags & OF_NoAccessSpecifier)) { if (FunctionClass & FC_Public) OS << "public: "; if (FunctionClass & FC_Protected) OS << "protected: "; if (FunctionClass & FC_Private) OS << "private: "; } if (!(Flags & OF_NoMemberType)) { if (!(FunctionClass & FC_Global)) { if (FunctionClass & FC_Static) OS << "static "; } if (FunctionClass & FC_Virtual) OS << "virtual "; if (FunctionClass & FC_ExternC) OS << "extern \"C\" "; } if (!(Flags & OF_NoReturnType) && ReturnType) { ReturnType->outputPre(OS, Flags); OS << " "; } if (!(Flags & OF_NoCallingConvention)) outputCallingConvention(OS, CallConvention); } void FunctionSignatureNode::outputPost(OutputStream &OS, OutputFlags Flags) const { if (!(FunctionClass & FC_NoParameterList)) { OS << "("; if (Params) Params->output(OS, Flags); else OS << "void"; if (IsVariadic) { if (OS.back() != '(') OS << ", "; OS << "..."; } OS << ")"; } if (Quals & Q_Const) OS << " const"; if (Quals & Q_Volatile) OS << " volatile"; if (Quals & Q_Restrict) OS << " __restrict"; if (Quals & Q_Unaligned) OS << " __unaligned"; if (IsNoexcept) OS << " noexcept"; if (RefQualifier == FunctionRefQualifier::Reference) OS << " &"; else if (RefQualifier == FunctionRefQualifier::RValueReference) OS << " &&"; if (!(Flags & OF_NoReturnType) && ReturnType) ReturnType->outputPost(OS, Flags); } void ThunkSignatureNode::outputPre(OutputStream &OS, OutputFlags Flags) const { OS << "[thunk]: "; FunctionSignatureNode::outputPre(OS, Flags); } void ThunkSignatureNode::outputPost(OutputStream &OS, OutputFlags Flags) const { if (FunctionClass & FC_StaticThisAdjust) { OS << "`adjustor{" << ThisAdjust.StaticOffset << "}'"; } else if (FunctionClass & FC_VirtualThisAdjust) { if (FunctionClass & FC_VirtualThisAdjustEx) { OS << "`vtordispex{" << ThisAdjust.VBPtrOffset << ", " << ThisAdjust.VBOffsetOffset << ", " << ThisAdjust.VtordispOffset << ", " << ThisAdjust.StaticOffset << "}'"; } else { OS << "`vtordisp{" << ThisAdjust.VtordispOffset << ", " << ThisAdjust.StaticOffset << "}'"; } } FunctionSignatureNode::outputPost(OS, Flags); } void PointerTypeNode::outputPre(OutputStream &OS, OutputFlags Flags) const { if (Pointee->kind() == NodeKind::FunctionSignature) { // If this is a pointer to a function, don't output the calling convention. // It needs to go inside the parentheses. const FunctionSignatureNode *Sig = static_cast(Pointee); Sig->outputPre(OS, OF_NoCallingConvention); } else Pointee->outputPre(OS, Flags); outputSpaceIfNecessary(OS); if (Quals & Q_Unaligned) OS << "__unaligned "; if (Pointee->kind() == NodeKind::ArrayType) { OS << "("; } else if (Pointee->kind() == NodeKind::FunctionSignature) { OS << "("; const FunctionSignatureNode *Sig = static_cast(Pointee); outputCallingConvention(OS, Sig->CallConvention); OS << " "; } if (ClassParent) { ClassParent->output(OS, Flags); OS << "::"; } switch (Affinity) { case PointerAffinity::Pointer: OS << "*"; break; case PointerAffinity::Reference: OS << "&"; break; case PointerAffinity::RValueReference: OS << "&&"; break; default: assert(false); } outputQualifiers(OS, Quals, false, false); } void PointerTypeNode::outputPost(OutputStream &OS, OutputFlags Flags) const { if (Pointee->kind() == NodeKind::ArrayType || Pointee->kind() == NodeKind::FunctionSignature) OS << ")"; Pointee->outputPost(OS, Flags); } void TagTypeNode::outputPre(OutputStream &OS, OutputFlags Flags) const { if (!(Flags & OF_NoTagSpecifier)) { switch (Tag) { OUTPUT_ENUM_CLASS_VALUE(TagKind, Class, "class"); OUTPUT_ENUM_CLASS_VALUE(TagKind, Struct, "struct"); OUTPUT_ENUM_CLASS_VALUE(TagKind, Union, "union"); OUTPUT_ENUM_CLASS_VALUE(TagKind, Enum, "enum"); } OS << " "; } QualifiedName->output(OS, Flags); outputQualifiers(OS, Quals, true, false); } void TagTypeNode::outputPost(OutputStream &OS, OutputFlags Flags) const {} void ArrayTypeNode::outputPre(OutputStream &OS, OutputFlags Flags) const { ElementType->outputPre(OS, Flags); outputQualifiers(OS, Quals, true, false); } void ArrayTypeNode::outputOneDimension(OutputStream &OS, OutputFlags Flags, Node *N) const { assert(N->kind() == NodeKind::IntegerLiteral); IntegerLiteralNode *ILN = static_cast(N); if (ILN->Value != 0) ILN->output(OS, Flags); } void ArrayTypeNode::outputDimensionsImpl(OutputStream &OS, OutputFlags Flags) const { if (Dimensions->Count == 0) return; outputOneDimension(OS, Flags, Dimensions->Nodes[0]); for (size_t I = 1; I < Dimensions->Count; ++I) { OS << "]["; outputOneDimension(OS, Flags, Dimensions->Nodes[I]); } } void ArrayTypeNode::outputPost(OutputStream &OS, OutputFlags Flags) const { OS << "["; outputDimensionsImpl(OS, Flags); OS << "]"; ElementType->outputPost(OS, Flags); } void SymbolNode::output(OutputStream &OS, OutputFlags Flags) const { Name->output(OS, Flags); } void FunctionSymbolNode::output(OutputStream &OS, OutputFlags Flags) const { Signature->outputPre(OS, Flags); outputSpaceIfNecessary(OS); Name->output(OS, Flags); Signature->outputPost(OS, Flags); } void VariableSymbolNode::output(OutputStream &OS, OutputFlags Flags) const { const char *AccessSpec = nullptr; bool IsStatic = true; switch (SC) { case StorageClass::PrivateStatic: AccessSpec = "private"; break; case StorageClass::PublicStatic: AccessSpec = "public"; break; case StorageClass::ProtectedStatic: AccessSpec = "protected"; break; default: IsStatic = false; break; } if (!(Flags & OF_NoAccessSpecifier) && AccessSpec) OS << AccessSpec << ": "; if (!(Flags & OF_NoMemberType) && IsStatic) OS << "static "; if (Type) { Type->outputPre(OS, Flags); outputSpaceIfNecessary(OS); } Name->output(OS, Flags); if (Type) Type->outputPost(OS, Flags); } void CustomTypeNode::outputPre(OutputStream &OS, OutputFlags Flags) const { Identifier->output(OS, Flags); } void CustomTypeNode::outputPost(OutputStream &OS, OutputFlags Flags) const {} void QualifiedNameNode::output(OutputStream &OS, OutputFlags Flags) const { Components->output(OS, Flags, "::"); } void RttiBaseClassDescriptorNode::output(OutputStream &OS, OutputFlags Flags) const { OS << "`RTTI Base Class Descriptor at ("; OS << NVOffset << ", " << VBPtrOffset << ", " << VBTableOffset << ", " << this->Flags; OS << ")'"; } void LocalStaticGuardVariableNode::output(OutputStream &OS, OutputFlags Flags) const { Name->output(OS, Flags); } void VcallThunkIdentifierNode::output(OutputStream &OS, OutputFlags Flags) const { OS << "`vcall'{" << OffsetInVTable << ", {flat}}"; } void SpecialTableSymbolNode::output(OutputStream &OS, OutputFlags Flags) const { outputQualifiers(OS, Quals, false, true); Name->output(OS, Flags); if (TargetName) { OS << "{for `"; TargetName->output(OS, Flags); OS << "'}"; } }