File: SymbolDisplay\SymbolDisplayVisitor.Types.vb
Web Access
Project: src\roslyn\src\Compilers\VisualBasic\Portable\Microsoft.CodeAnalysis.VisualBasic.vbproj (Microsoft.CodeAnalysis.VisualBasic)
' Licensed to the .NET Foundation under one or more agreements.
' The .NET Foundation licenses this file to you under the MIT license.
' See the LICENSE file in the project root for more information.

Imports System.Collections.Immutable
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols

Namespace Microsoft.CodeAnalysis.VisualBasic
    Partial Friend Class SymbolDisplayVisitor
        Public Overrides Sub VisitArrayType(symbol As IArrayTypeSymbol)
            'See spec section 12.1 for the order of rank specifiers
            'e.g. int[][,][,,] is stored as
            '     ArrayType
            '         Rank = 1
            '         ElementType = ArrayType
            '             Rank = 2
            '             ElementType = ArrayType
            '                 Rank = 3
            '                 ElementType = int

            If Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.ReverseArrayRankSpecifiers) Then
                ' Ironically, reverse order is simpler - we just have to recurse on the element type and then add a rank specifier.
                symbol.ElementType.Accept(Me)
                AddCustomModifiersIfRequired(symbol.CustomModifiers, trailingSpace:=True)
                AddArrayRank(symbol)
                Return
            End If

            Dim underlyingNonArrayType As ITypeSymbol = symbol.ElementType
            While underlyingNonArrayType.Kind = SymbolKind.ArrayType
                underlyingNonArrayType = DirectCast(underlyingNonArrayType, IArrayTypeSymbol).ElementType
            End While

            underlyingNonArrayType.Accept(Me.NotFirstVisitor())

            Dim arrayType As IArrayTypeSymbol = symbol
            While arrayType IsNot Nothing
                AddCustomModifiersIfRequired(arrayType.CustomModifiers, trailingSpace:=True)

                AddArrayRank(arrayType)
                arrayType = TryCast(arrayType.ElementType, IArrayTypeSymbol)
            End While
        End Sub

        Private Sub AddArrayRank(symbol As IArrayTypeSymbol)
            Dim insertStars As Boolean = Format.MiscellaneousOptions.IncludesOption(SymbolDisplayMiscellaneousOptions.UseAsterisksInMultiDimensionalArrays)
            AddPunctuation(SyntaxKind.OpenParenToken)
            If symbol.Rank > 1 Then
                If insertStars Then
                    AddPunctuation(SyntaxKind.AsteriskToken)
                End If
            Else
                Dim array = TryCast(symbol, ArrayTypeSymbol)

                If array IsNot Nothing AndAlso Not array.IsSZArray Then
                    ' Always add an asterisk in this case in order to distinguish between SZArray and MDArray.
                    AddPunctuation(SyntaxKind.AsteriskToken)
                End If
            End If

            Dim i As Integer = 0
            While i < symbol.Rank - 1
                AddPunctuation(SyntaxKind.CommaToken)
                If insertStars Then
                    AddPunctuation(SyntaxKind.AsteriskToken)
                End If
                i = i + 1
            End While
            AddPunctuation(SyntaxKind.CloseParenToken)
        End Sub

        Public Overrides Sub VisitDynamicType(symbol As IDynamicTypeSymbol)
            AddKeyword(SyntaxKind.ObjectKeyword)
        End Sub

        Public Overrides Sub VisitPointerType(symbol As IPointerTypeSymbol)
            symbol.PointedAtType.Accept(Me.NotFirstVisitor())
            AddPunctuation(SyntaxKind.AsteriskToken)
        End Sub

        Public Overrides Sub VisitTypeParameter(symbol As ITypeParameterSymbol)
            If IsFirstSymbolVisited Then
                AddTypeParameterVarianceIfRequired(symbol)
            End If

            Builder.Add(CreatePart(SymbolDisplayPartKind.TypeParameterName, symbol, symbol.Name, False))
        End Sub

        Public Overrides Sub VisitNamedType(symbol As INamedTypeSymbol)
            If Me.IsMinimizing AndAlso TryAddAlias(symbol, Builder) Then
                Return
            End If

            If Format.MiscellaneousOptions.IncludesOption(SymbolDisplayMiscellaneousOptions.UseSpecialTypes) Then
                If AddSpecialTypeKeyword(symbol) Then
                    Return
                End If

                If Not Format.MiscellaneousOptions.IncludesOption(SymbolDisplayMiscellaneousOptions.ExpandNullable) Then
                    If ITypeSymbolHelpers.IsNullableType(symbol) AndAlso symbol IsNot symbol.OriginalDefinition Then
                        symbol.TypeArguments(0).Accept(Me.NotFirstVisitor())
                        AddPunctuation(SyntaxKind.QuestionToken)
                        Return
                    End If
                End If
            End If

            If Me.IsMinimizing OrElse (symbol.IsTupleType AndAlso Not ShouldDisplayAsValueTuple(symbol)) Then
                MinimallyQualify(symbol)
                Return
            End If

            AddTypeKind(symbol)

            If CanShowDelegateSignature(symbol) Then
                If symbol.IsAnonymousType OrElse
                   Format.DelegateStyle = SymbolDisplayDelegateStyle.NameAndSignature Then

                    Dim invokeMethod = symbol.DelegateInvokeMethod
                    If invokeMethod.ReturnsVoid Then
                        AddKeyword(SyntaxKind.SubKeyword)
                    Else
                        If invokeMethod.ReturnsByRef AndAlso Format.MemberOptions.IncludesOption(SymbolDisplayMemberOptions.IncludeRef) Then
                            AddKeyword(SyntaxKind.ByRefKeyword)
                            AddSpace()
                        End If

                        AddKeyword(SyntaxKind.FunctionKeyword)
                    End If

                    AddSpace()
                End If
            End If

            'TODO: aliases

            'only visit the namespace if the style requires it and there isn't an enclosing type
            Dim visitedParents = False
            Dim containingSymbol As ISymbol = symbol.ContainingSymbol
            If ShouldVisitNamespace(containingSymbol) Then
                visitedParents = True

                Dim ns = DirectCast(containingSymbol, INamespaceSymbol)

                ' For some VB symbols, we may want to fix up the namespace name (to normalize case)
                ' GetEmittedNamespaceName may return Nothing
                Dim vbNamedTypeSymbol = TryCast(symbol, NamedTypeSymbol)
                Dim emittedName As String = If(
                    vbNamedTypeSymbol IsNot Nothing,
                    If(vbNamedTypeSymbol.GetEmittedNamespaceName(), String.Empty),
                                     String.Empty)

                VisitNamespace(ns, emittedName)
                AddOperator(SyntaxKind.DotToken)
            End If

            If Format.TypeQualificationStyle = SymbolDisplayTypeQualificationStyle.NameAndContainingTypes OrElse
                Format.TypeQualificationStyle = SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces Then
                Dim containingType = symbol.ContainingType
                If containingType IsNot Nothing Then
                    visitedParents = True
                    containingType.Accept(Me.NotFirstVisitor())
                    AddNestedTypeSeparator()
                End If
            End If

            AddNameAndTypeArgumentsOrParameters(symbol, visitedParents)

            If CanShowDelegateSignature(symbol) Then
                If symbol.IsAnonymousType OrElse
                   Format.DelegateStyle = SymbolDisplayDelegateStyle.NameAndSignature OrElse
                   Format.DelegateStyle = SymbolDisplayDelegateStyle.NameAndParameters Then

                    Dim method = symbol.DelegateInvokeMethod
                    AddPunctuation(SyntaxKind.OpenParenToken)
                    AddParametersIfRequired(isExtensionMethod:=False, parameters:=method.Parameters)
                    AddPunctuation(SyntaxKind.CloseParenToken)
                End If

                If symbol.IsAnonymousType OrElse Format.DelegateStyle = SymbolDisplayDelegateStyle.NameAndSignature Then
                    Dim invokeMethod = symbol.DelegateInvokeMethod
                    If Not invokeMethod.ReturnsVoid Then
                        AddSpace()
                        AddKeyword(SyntaxKind.AsKeyword)
                        AddSpace()
                        invokeMethod.ReturnType.Accept(Me.NotFirstVisitor())
                    End If
                End If
            End If
        End Sub

        Private Sub AddNestedTypeSeparator()
            If Format.CompilerInternalOptions.HasFlag(SymbolDisplayCompilerInternalOptions.UsePlusForNestedTypes) Then
                AddOperator(SyntaxKind.PlusToken)
            Else
                AddOperator(SyntaxKind.DotToken)
            End If
        End Sub

        Private Function CanShowDelegateSignature(symbol As INamedTypeSymbol) As Boolean
            Return IsFirstSymbolVisited AndAlso
                symbol.TypeKind = TypeKind.Delegate AndAlso
                (symbol.IsAnonymousType OrElse Format.DelegateStyle <> SymbolDisplayDelegateStyle.NameOnly) AndAlso
                symbol.DelegateInvokeMethod IsNot Nothing
        End Function

        Private Sub AddNameAndTypeArgumentsOrParameters(symbol As INamedTypeSymbol, noEscaping As Boolean)
            Dim partKind As SymbolDisplayPartKind
            Dim symbolName As String = Nothing
            Dim skipTypeArguments As Boolean = False

            If symbol.IsAnonymousType Then
                AddAnonymousTypeName(symbol)
                Return
            ElseIf symbol.IsTupleType Then
                ' If top level tuple uses non-default names, there is no way to preserve them
                ' unless we use tuple syntax for the type. So, we give them priority.
                If Not ShouldDisplayAsValueTuple(symbol) Then
                    AddTupleTypeName(symbol)
                    Return
                End If

                ' Fall back to displaying the underlying type.
                symbol = symbol.TupleUnderlyingType
            End If

            ' It would be nice to handle C# NoPia symbols too, but it's not worth the effort.

            Dim illegalGenericInstantiationSymbol = TryCast(symbol, NoPiaIllegalGenericInstantiationSymbol)
            If illegalGenericInstantiationSymbol IsNot Nothing Then
                symbol = illegalGenericInstantiationSymbol.UnderlyingSymbol
            Else
                Dim ambiguousCanonicalTypeSymbol = TryCast(symbol, NoPiaAmbiguousCanonicalTypeSymbol)
                If ambiguousCanonicalTypeSymbol IsNot Nothing Then
                    symbol = ambiguousCanonicalTypeSymbol.FirstCandidate
                Else
                    Dim missingCanonicalTypeSymbol = TryCast(symbol, NoPiaMissingCanonicalTypeSymbol)
                    If missingCanonicalTypeSymbol IsNot Nothing Then
                        symbolName = missingCanonicalTypeSymbol.FullTypeName
                    End If
                End If
            End If

            If symbolName Is Nothing Then
                symbolName = If(symbol.IsExtension, symbol.ExtensionGroupingName, symbol.Name)
            End If

            If Format.MiscellaneousOptions.IncludesOption(SymbolDisplayMiscellaneousOptions.UseErrorTypeSymbolName) AndAlso
                String.IsNullOrEmpty(symbolName) Then

                symbolName = StringConstants.NamedSymbolErrorName
            Else
                symbolName = RemoveAttributeSuffixIfNecessary(symbol, symbolName)
            End If

            Select Case symbol.TypeKind
                Case TypeKind.Class,
                     TypeKind.Submission,
                     TypeKind.Extension
                    partKind = SymbolDisplayPartKind.ClassName
                Case TypeKind.Delegate
                    partKind = SymbolDisplayPartKind.DelegateName
                Case TypeKind.Enum
                    partKind = SymbolDisplayPartKind.EnumName
                Case TypeKind.Interface
                    partKind = SymbolDisplayPartKind.InterfaceName
                Case TypeKind.Module
                    partKind = SymbolDisplayPartKind.ModuleName
                Case TypeKind.Struct
                    partKind = SymbolDisplayPartKind.StructName
                Case TypeKind.Error
                    partKind = SymbolDisplayPartKind.ErrorTypeName
                Case Else
                    Throw ExceptionUtilities.UnexpectedValue(symbol.TypeKind)
            End Select

            Builder.Add(CreatePart(partKind, symbol, symbolName, noEscaping))

            ' Unfortunately, this will only work for VB symbols.  The degraded experience for non-VB symbols seems acceptable for now.
            Dim isMissingMetadataType As Boolean = TypeOf symbol Is MissingMetadataTypeSymbol

            If Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseArityForGenericTypes) Then
                If symbol.Arity > 0 Then
                    Dim suffix As String = MetadataHelpers.GetAritySuffix(symbol.Arity)
                    Dim vbNamedType = TryCast(symbol, NamedTypeSymbol)

                    If If(vbNamedType IsNot Nothing, vbNamedType.MangleName, symbol.MetadataName.Equals(symbol.Name + suffix)) Then
                        Builder.Add(CreatePart(InternalSymbolDisplayPartKind.Arity, Nothing,
                                           suffix, False))
                    End If
                End If
            ElseIf symbol.Arity > 0 AndAlso Format.GenericsOptions.IncludesOption(SymbolDisplayGenericsOptions.IncludeTypeParameters) AndAlso Not skipTypeArguments Then
                If isMissingMetadataType OrElse symbol.IsUnboundGenericType Then
                    AddPunctuation(SyntaxKind.OpenParenToken)
                    AddKeyword(SyntaxKind.OfKeyword)
                    AddSpace()
                    Dim i As Integer = 0
                    While i < symbol.Arity - 1
                        AddPunctuation(SyntaxKind.CommaToken)
                        i = i + 1
                    End While
                    AddPunctuation(SyntaxKind.CloseParenToken)
                Else
                    AddTypeArguments(symbol.TypeArguments, symbol)
                End If
            End If

            ' Only the compiler can set the internal option and the compiler doesn't use other implementations of INamedTypeSymbol.
            If Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.FlagMissingMetadataTypes) AndAlso
                (isMissingMetadataType OrElse
                 (Not symbol.IsDefinition AndAlso TypeOf symbol.OriginalDefinition Is MissingMetadataTypeSymbol)) Then

                Builder.Add(CreatePart(SymbolDisplayPartKind.Punctuation, Nothing, "[", False))
                Builder.Add(CreatePart(InternalSymbolDisplayPartKind.Other, symbol, "missing", False))
                Builder.Add(CreatePart(SymbolDisplayPartKind.Punctuation, Nothing, "]", False))
            End If
        End Sub

        Private Function ShouldDisplayAsValueTuple(symbol As INamedTypeSymbol) As Boolean
            Debug.Assert(symbol.IsTupleType)

            If Format.MiscellaneousOptions.IncludesOption(SymbolDisplayMiscellaneousOptions.ExpandValueTuple) Then
                Return True
            End If

            Return Not (HasNonDefaultTupleElements(symbol) OrElse CanUseTupleTypeName(symbol))
        End Function

        Private Sub AddAnonymousTypeName(symbol As INamedTypeSymbol)
            Select Case symbol.TypeKind
                Case TypeKind.Class
                    ' TODO: revise to generate user-friendly name 
                    Dim members = String.Join(", ", symbol.GetMembers().OfType(Of IPropertySymbol).Select(Function(p) CreateAnonymousTypeMember(p)))

                    If members.Length = 0 Then
                        Builder.Add(New SymbolDisplayPart(SymbolDisplayPartKind.ClassName, symbol, "<empty anonymous type>"))
                    Else
                        Dim name = String.Format("<anonymous type: {0}>", members)
                        Builder.Add(New SymbolDisplayPart(SymbolDisplayPartKind.ClassName, symbol, name))
                    End If

                Case TypeKind.Delegate
                    Builder.Add(CreatePart(SymbolDisplayPartKind.DelegateName, symbol, "<generated method>", True))
            End Select
        End Sub

        ''' <summary>
        ''' Returns true if tuple type syntax can be used to refer to the tuple type without loss of information.
        ''' For example, it cannot be used when extension tuple is using non-default friendly names. 
        ''' </summary>
        ''' <param name="tupleSymbol"></param>
        ''' <returns></returns>
        Private Shared Function CanUseTupleTypeName(tupleSymbol As INamedTypeSymbol) As Boolean
            Dim currentUnderlying As INamedTypeSymbol = GetTupleUnderlyingTypeOrSelf(tupleSymbol)

            If currentUnderlying.Arity = 1 Then
                Return False
            End If

            While currentUnderlying.Arity = TupleTypeSymbol.RestPosition
                tupleSymbol = DirectCast(currentUnderlying.TypeArguments(TupleTypeSymbol.RestPosition - 1), INamedTypeSymbol)
                Debug.Assert(tupleSymbol.IsTupleType)

                If HasNonDefaultTupleElements(tupleSymbol) Then
                    Return False
                End If

                currentUnderlying = GetTupleUnderlyingTypeOrSelf(tupleSymbol)
            End While

            Return True
        End Function

        Private Shared Function GetTupleUnderlyingTypeOrSelf(tupleSymbol As INamedTypeSymbol) As INamedTypeSymbol
            Return If(tupleSymbol.TupleUnderlyingType, tupleSymbol)
        End Function

        Private Shared Function HasNonDefaultTupleElements(tupleSymbol As INamedTypeSymbol) As Boolean
            Return tupleSymbol.TupleElements.Any(Function(e) e.IsExplicitlyNamedTupleElement)
        End Function

        Private Sub AddTupleTypeName(symbol As INamedTypeSymbol)
            Debug.Assert(symbol.IsTupleType)

            If Me.Format.MiscellaneousOptions.IncludesOption(SymbolDisplayMiscellaneousOptions.CollapseTupleTypes) Then
                Builder.Add(CreatePart(SymbolDisplayPartKind.StructName, symbol, "<tuple>", noEscaping:=True))
                Return
            End If

            Dim elements As ImmutableArray(Of IFieldSymbol) = symbol.TupleElements

            AddPunctuation(SyntaxKind.OpenParenToken)

            For i As Integer = 0 To elements.Length - 1
                Dim element = elements(i)

                If i <> 0 Then
                    AddPunctuation(SyntaxKind.CommaToken)
                    AddSpace()
                End If

                If element.IsExplicitlyNamedTupleElement Then
                    Builder.Add(CreatePart(SymbolDisplayPartKind.FieldName, element, element.Name, noEscaping:=False))
                    AddSpace()
                    AddKeyword(SyntaxKind.AsKeyword)
                    AddSpace()
                End If

                element.Type.Accept(Me.NotFirstVisitor)
            Next

            AddPunctuation(SyntaxKind.CloseParenToken)
        End Sub

        Private Function CreateAnonymousTypeMember(prop As IPropertySymbol) As String
            Dim result = CreateAnonymousTypeMemberWorker(prop)
            Return If(prop.IsReadOnly, "Key " & result, result)
        End Function

        Private Function CreateAnonymousTypeMemberWorker(prop As IPropertySymbol) As String
            Return prop.Name & " As " & prop.Type.ToDisplayString(Format)
        End Function

        Private Function AddSpecialTypeKeyword(symbol As INamedTypeSymbol) As Boolean
            Dim type = symbol.SpecialType
            Dim specialTypeName = type.TryGetKeywordText()
            If specialTypeName Is Nothing Then
                Return False
            End If

            Builder.Add(CreatePart(SymbolDisplayPartKind.Keyword, symbol, specialTypeName, False))
            Return True
        End Function

        Private Sub AddTypeKind(symbol As INamedTypeSymbol)
            If IsFirstSymbolVisited AndAlso Format.KindOptions.IncludesOption(SymbolDisplayKindOptions.IncludeTypeKeyword) Then
                If symbol.IsAnonymousType Then
                    ' NOTE: Not actually a keyword, but it's not worth introducing a new kind just for this.
                    Builder.Add(New SymbolDisplayPart(SymbolDisplayPartKind.AnonymousTypeIndicator, Nothing, "AnonymousType"))
                    AddSpace()
                ElseIf symbol.IsTupleType Then
                    Builder.Add(New SymbolDisplayPart(SymbolDisplayPartKind.AnonymousTypeIndicator, Nothing, "Tuple"))
                    AddSpace()
                Else
                    Dim keyword = GetTypeKindKeyword(symbol.TypeKind)
                    If keyword = SyntaxKind.None Then
                        Return
                    End If

                    AddKeyword(keyword)
                    AddSpace()
                End If
            End If
        End Sub

        Private Shared Function GetTypeKindKeyword(typeKind As TypeKind) As SyntaxKind
            Select Case typeKind
                Case TypeKind.Enum
                    Return SyntaxKind.EnumKeyword
                Case TypeKind.Class
                    Return SyntaxKind.ClassKeyword
                Case TypeKind.Delegate
                    Return SyntaxKind.DelegateKeyword
                Case TypeKind.Interface
                    Return SyntaxKind.InterfaceKeyword
                Case TypeKind.Module
                    Return SyntaxKind.ModuleKeyword
                Case TypeKind.Struct
                    Return SyntaxKind.StructureKeyword
                Case Else
                    Return SyntaxKind.None
            End Select
        End Function

        Private Sub AddTypeParameterVarianceIfRequired(symbol As ITypeParameterSymbol)
            If Format.GenericsOptions.IncludesOption(SymbolDisplayGenericsOptions.IncludeVariance) Then
                Select Case symbol.Variance
                    Case VarianceKind.In
                        AddKeyword(SyntaxKind.InKeyword)
                        AddSpace()
                    Case VarianceKind.Out
                        AddKeyword(SyntaxKind.OutKeyword)
                        AddSpace()
                End Select
            End If
        End Sub

        Private Sub AddTypeArguments(typeArguments As ImmutableArray(Of ITypeSymbol),
                                     Optional modifiersSource As INamedTypeSymbol = Nothing)
            AddPunctuation(SyntaxKind.OpenParenToken)
            AddKeyword(SyntaxKind.OfKeyword)
            AddSpace()

            Dim first As Boolean = True
            For i As Integer = 0 To typeArguments.Length - 1
                Dim typeArg = typeArguments(i)

                If Not first Then
                    AddPunctuation(SyntaxKind.CommaToken)
                    AddSpace()
                End If

                first = False

                If typeArg.Kind = SymbolKind.TypeParameter Then
                    Dim typeParam = DirectCast(typeArg, ITypeParameterSymbol)
                    AddTypeParameterVarianceIfRequired(typeParam)
                    typeParam.Accept(Me.NotFirstVisitor())

                    If Format.GenericsOptions.IncludesOption(SymbolDisplayGenericsOptions.IncludeTypeConstraints) Then
                        AddTypeParameterConstraints(typeParam)
                    End If
                Else
                    typeArg.Accept(Me.NotFirstVisitorNamespaceOrType())
                End If

                If modifiersSource IsNot Nothing Then
                    AddCustomModifiersIfRequired(modifiersSource.GetTypeArgumentCustomModifiers(i))
                End If
            Next

            AddPunctuation(SyntaxKind.CloseParenToken)
        End Sub

        ''' <summary>
        ''' Return the number of special constraints ('Class', 'Structure',
        ''' and 'New') associated with the type parameter.
        ''' </summary>
        Private Shared Function TypeParameterSpecialConstraintCount(typeParam As ITypeParameterSymbol) As Integer
            Return If(typeParam.HasReferenceTypeConstraint, 1, 0) +
                If(typeParam.HasValueTypeConstraint, 1, 0) +
                If(typeParam.HasConstructorConstraint, 1, 0)
        End Function

        Private Sub AddTypeParameterConstraints(typeParam As ITypeParameterSymbol)
            If Not IsFirstSymbolVisited Then
                Return
            End If

            Dim constraintTypes = typeParam.ConstraintTypes
            Dim constraintCount = TypeParameterSpecialConstraintCount(typeParam) + constraintTypes.Length

            If constraintCount = 0 Then
                Return
            End If

            AddSpace()
            AddKeyword(SyntaxKind.AsKeyword)
            AddSpace()

            If constraintCount > 1 Then
                AddPunctuation(SyntaxKind.OpenBraceToken)
            End If

            Dim needComma As Boolean = False
            If typeParam.HasReferenceTypeConstraint Then
                AddKeyword(SyntaxKind.ClassKeyword)
                needComma = True
            ElseIf typeParam.HasValueTypeConstraint Then
                AddKeyword(SyntaxKind.StructureKeyword)
                needComma = True
            End If

            For Each baseType In constraintTypes
                If needComma Then
                    AddPunctuation(SyntaxKind.CommaToken)
                    AddSpace()
                End If

                baseType.Accept(Me.NotFirstVisitor())
                needComma = True
            Next

            If typeParam.HasConstructorConstraint Then
                If needComma Then
                    AddPunctuation(SyntaxKind.CommaToken)
                    AddSpace()
                End If
                AddKeyword(SyntaxKind.NewKeyword)
            End If

            If constraintCount > 1 Then
                AddPunctuation(SyntaxKind.CloseBraceToken)
            End If
        End Sub

        Friend Sub AddExtensionMarkerName(extension As INamedTypeSymbol)
            Debug.Assert(extension.IsExtension)
            AddNestedTypeSeparator()
            Builder.Add(CreatePart(SymbolDisplayPartKind.ClassName, extension, extension.ExtensionMarkerName, False))
        End Sub
    End Class
End Namespace