File: SymbolDisplay\SymbolDisplayVisitor.Types.vb
Web Access
Project: src\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())
 
                    If Format.CompilerInternalOptions.HasFlag(SymbolDisplayCompilerInternalOptions.UsePlusForNestedTypes) Then
                        AddOperator(SyntaxKind.PlusToken)
                    Else
                        AddOperator(SyntaxKind.DotToken)
                    End If
                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 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 = 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
                    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
                ' Only the compiler can set the internal option and the compiler doesn't use other implementations of INamedTypeSymbol.
                If DirectCast(symbol, NamedTypeSymbol).MangleName Then
                    Debug.Assert(symbol.Arity > 0)
                    Builder.Add(CreatePart(InternalSymbolDisplayPartKind.Arity, Nothing,
                                           MetadataHelpers.GenericTypeNameManglingChar & symbol.Arity.ToString(), False))
                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
 
    End Class
End Namespace