' 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.Generic
Imports System.Globalization
Imports System.Runtime.InteropServices
Imports System.Threading
Imports Microsoft.CodeAnalysis.Collections
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Imports TypeKind = Microsoft.CodeAnalysis.TypeKind

Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols
    Friend Class SourceMemberFieldSymbol
        Inherits SourceFieldSymbol

        ' The type of the field. Set to Nothing if not computed yet.
        Private _lazyType As TypeSymbol

        Private _lazyMeParameter As ParameterSymbol

        Protected Sub New(container As SourceMemberContainerTypeSymbol,
                          syntaxRef As SyntaxReference,
                          name As String,
                          memberFlags As SourceMemberFlags)

            MyBase.New(container, syntaxRef, name, memberFlags)
        End Sub

        Protected Overrides Sub GenerateDeclarationErrorsImpl(cancellationToken As CancellationToken)
            MyBase.GenerateDeclarationErrorsImpl(cancellationToken)

            If (_lazyState And StateFlags.TypeConstraintsChecked) = 0 Then
                Dim sourceModule = DirectCast(Me.ContainingModule, SourceModuleSymbol)
                Dim diagnostics = BindingDiagnosticBag.GetInstance()
                Type.CheckAllConstraints(DeclaringCompilation.LanguageVersion,
                                     GetFirstLocation(), diagnostics, template:=New CompoundUseSiteInfo(Of AssemblySymbol)(diagnostics, sourceModule.ContainingAssembly))
                sourceModule.AtomicSetFlagAndStoreDiagnostics(_lazyState, StateFlags.TypeConstraintsChecked, 0, diagnostics)
                diagnostics.Free()
            End If
        End Sub

        Friend NotOverridable Overrides ReadOnly Property DeclarationSyntax As VisualBasicSyntaxNode
            Get
                Return Syntax.Parent.Parent
            End Get
        End Property

        Friend NotOverridable Overrides Function IsDefinedInSourceTree(tree As SyntaxTree, definedWithinSpan As TextSpan?, Optional cancellationToken As CancellationToken = Nothing) As Boolean
            Return IsDefinedInSourceTree(Me.DeclarationSyntax, tree, definedWithinSpan, cancellationToken)
        End Function

        Friend NotOverridable Overrides ReadOnly Property GetAttributeDeclarations As OneOrMany(Of SyntaxList(Of AttributeListSyntax))
            Get
                Return OneOrMany.Create(DirectCast(Syntax.Parent.Parent, FieldDeclarationSyntax).AttributeLists)
            End Get
        End Property

        Friend Overrides ReadOnly Property MeParameter As ParameterSymbol
            Get
                If IsShared Then
                    Return Nothing
                Else
                    If _lazyMeParameter Is Nothing Then
                        Interlocked.CompareExchange(Of ParameterSymbol)(_lazyMeParameter, New MeParameterSymbol(Me), Nothing)
                    End If

                    Return _lazyMeParameter
                End If
            End Get
        End Property

        Public Overrides ReadOnly Property Type As TypeSymbol
            Get
                If _lazyType Is Nothing Then
                    Dim sourceModule = DirectCast(Me.ContainingModule, SourceModuleSymbol)
                    Dim diagnostics = BindingDiagnosticBag.GetInstance()
                    Dim varType = ComputeType(diagnostics)
                    Debug.Assert(varType IsNot Nothing)
                    sourceModule.AtomicStoreReferenceAndDiagnostics(_lazyType, varType, diagnostics)
                    diagnostics.Free()
                End If

                Return _lazyType
            End Get
        End Property

        Private Function ComputeType(diagBag As BindingDiagnosticBag) As TypeSymbol
            Dim declaredType = GetDeclaredType(diagBag)  ' needed for diagnostic creation in all cases

            If Not HasDeclaredType Then
                Return GetInferredType(ConstantFieldsInProgress.Empty)
            Else
                Return declaredType
            End If
        End Function

        Private Function GetDeclaredType(diagBag As BindingDiagnosticBag) As TypeSymbol
            Dim modifiedIdentifier As ModifiedIdentifierSyntax = DirectCast(Syntax, ModifiedIdentifierSyntax)
            Dim declarator = DirectCast(modifiedIdentifier.Parent, VariableDeclaratorSyntax)

            Dim binder As Binder = BinderBuilder.CreateBinderForType(
                DirectCast(Me.ContainingModule, SourceModuleSymbol),
                Me.SyntaxTree,
                ContainingType)

            binder = New LocationSpecificBinder(BindingLocation.FieldType, Me, binder)

            ' Ignore type syntax errors for all but the first field of a given type.
            Dim varType = ComputeFieldType(modifiedIdentifier, binder, diagBag,
                                           isConst:=Me.IsConst,
                                           isWithEvents:=False,
                                           ignoreTypeSyntaxDiagnostics:=(m_memberFlags And SourceMemberFlags.FirstFieldDeclarationOfType) = 0)

            If Not varType.IsErrorType() Then
                If Me.IsConst Then
                    ' Note: The native compiler reports two errors for "Const F As C": ERR_ConstAsNonConstant
                    ' and ERR_ConstantWithNoValue. Rather than report two errors in those cases, we only
                    ' report ERR_ConstantWithNoValue if ERR_ConstAsNonConstant has not been reported.
                    ' In other words, we only report "Constants must have a value" for fields with missing
                    ' initializer if the field type can be "Const".

                    If Not varType.IsValidTypeForConstField() Then
                        ' "Constants must be of an intrinsic or enumerated type, not a class, structure, type parameter, or array type."
                        If varType.IsArrayType Then
                            ' arrays get the squiggles under the identifier name
                            binder.ReportDiagnostic(diagBag, modifiedIdentifier.Identifier, ERRID.ERR_ConstAsNonConstant)
                        Else
                            ' other data types get the squiggles under the type part of the as clause 
                            binder.ReportDiagnostic(diagBag, declarator.AsClause.Type, ERRID.ERR_ConstAsNonConstant)
                        End If
                    ElseIf declarator.Initializer Is Nothing Then
                        ' "Constants must have a value."
                        binder.ReportDiagnostic(diagBag, modifiedIdentifier, ERRID.ERR_ConstantWithNoValue)
                    End If

                Else
                    Dim restrictedType As TypeSymbol = Nothing
                    If varType.IsRestrictedTypeOrArrayType(restrictedType) Then
                        binder.ReportDiagnostic(diagBag, declarator.AsClause.Type, ERRID.ERR_RestrictedType1, restrictedType)
                    End If
                End If

                If HasDeclaredType Then
                    Dim errorLocation = SourceSymbolHelpers.GetAsClauseLocation(modifiedIdentifier.Identifier, declarator.AsClause)
                    AccessCheck.VerifyAccessExposureForMemberType(Me, errorLocation, varType, diagBag)
                End If
            End If
            Return varType
        End Function

        ' Helper used for computing the type of a field.
        Private Shared Function ComputeFieldType(modifiedIdentifierSyntax As ModifiedIdentifierSyntax,
                                                 binder As Binder,
                                                 diagnostics As BindingDiagnosticBag,
                                                 isConst As Boolean,
                                                 isWithEvents As Boolean,
                                                 ignoreTypeSyntaxDiagnostics As Boolean) As TypeSymbol
            Dim declarator = DirectCast(modifiedIdentifierSyntax.Parent, VariableDeclaratorSyntax)

            Dim asClauseOpt = declarator.AsClause
            Dim asClauseType As TypeSymbol = Nothing
            Dim initializerSyntax As VisualBasicSyntaxNode = declarator.Initializer

            If asClauseOpt IsNot Nothing Then
                If (asClauseOpt.Kind <> SyntaxKind.AsNewClause OrElse (DirectCast(asClauseOpt, AsNewClauseSyntax).NewExpression.Kind <> SyntaxKind.AnonymousObjectCreationExpression)) Then
                    asClauseType = binder.BindTypeSyntax(asClauseOpt.Type, If(ignoreTypeSyntaxDiagnostics, BindingDiagnosticBag.Discarded, diagnostics))
                End If
                If asClauseOpt.Kind = SyntaxKind.AsNewClause Then
                    initializerSyntax = asClauseOpt
                End If
            End If

            Dim omitFurtherDiagnostics As Boolean = String.IsNullOrEmpty(modifiedIdentifierSyntax.Identifier.ValueText)

            Dim varType As TypeSymbol
            If (asClauseOpt IsNot Nothing AndAlso asClauseOpt.Kind = SyntaxKind.AsNewClause AndAlso
             (DirectCast(asClauseOpt, AsNewClauseSyntax).NewExpression.Kind = SyntaxKind.AnonymousObjectCreationExpression)) Then
                varType = ErrorTypeSymbol.UnknownResultType
            Else
                Dim getErrorInfo As Func(Of DiagnosticInfo) = Nothing

                If Not omitFurtherDiagnostics AndAlso Not (isConst AndAlso binder.OptionInfer) Then

                    If isWithEvents Then
                        'WithEvents' variables must have an 'As' clause.
                        getErrorInfo = ErrorFactory.GetErrorInfo_ERR_WithEventsRequiresClass
                    Else
                        If binder.OptionStrict = OptionStrict.On Then
                            getErrorInfo = ErrorFactory.GetErrorInfo_ERR_StrictDisallowImplicitObject
                        ElseIf binder.OptionStrict = OptionStrict.Custom Then
                            getErrorInfo = ErrorFactory.GetErrorInfo_WRN_ObjectAssumedVar1_WRN_MissingAsClauseinVarDecl
                        End If
                    End If
                End If

                varType = binder.DecodeModifiedIdentifierType(modifiedIdentifierSyntax, asClauseType, asClauseOpt, initializerSyntax,
                                                                  getErrorInfo,
                                                                  diagnostics,
                                                                  VisualBasic.Binder.ModifiedIdentifierTypeDecoderContext.FieldType)
            End If

            Debug.Assert(varType IsNot Nothing)
            Return varType
        End Function

        Friend Shared Function ComputeWithEventsFieldType(propertySymbol As PropertySymbol,
                                                          modifiedIdentifier As ModifiedIdentifierSyntax,
                                                          binder As Binder,
                                                          ignoreTypeSyntaxDiagnostics As Boolean,
                                                          diagnostics As BindingDiagnosticBag) As TypeSymbol

            Dim varType = ComputeFieldType(modifiedIdentifier, binder, diagnostics, isConst:=False, isWithEvents:=True, ignoreTypeSyntaxDiagnostics:=ignoreTypeSyntaxDiagnostics)

            If Not varType.IsErrorType() Then
                Dim declarator = DirectCast(modifiedIdentifier.Parent, VariableDeclaratorSyntax)
                Dim identifier = modifiedIdentifier.Identifier

                ' it must be either a class or an interface or a type parameter
                ' with class constraints.
                ' and for arrays there is a special error.
                If varType.IsArrayType Then
                    ' 'WithEvents' variables cannot be typed as arrays.
                    binder.ReportDiagnostic(diagnostics, modifiedIdentifier, ERRID.ERR_EventSourceIsArray)

                ElseIf Not (varType.IsClassOrInterfaceType OrElse
                    (varType.Kind = SymbolKind.TypeParameter AndAlso varType.IsReferenceType)) Then

                    If declarator.AsClause IsNot Nothing Then
                        Dim typeSyntax = declarator.AsClause.Type
                        If typeSyntax IsNot Nothing Then
                            ' 'WithEvents' variables can only be typed as classes, interfaces or type parameters with class constraints.
                            binder.ReportDiagnostic(diagnostics, identifier, ERRID.ERR_WithEventsAsStruct)
                        End If
                    End If
                End If

                If declarator.AsClause IsNot Nothing Then
                    Dim errorLocation = SourceSymbolHelpers.GetAsClauseLocation(identifier, declarator.AsClause)
                    AccessCheck.VerifyAccessExposureForMemberType(propertySymbol, errorLocation, varType, diagnostics)
                End If
            End If

            Return varType
        End Function

        ''' <summary>
        ''' Gets the inferred type of this const field from the initialization value.
        ''' </summary>
        ''' <param name="inProgress">Used to detect dependencies between constant field values.</param><returns></returns>
        Friend Overrides Function GetInferredType(inProgress As ConstantFieldsInProgress) As TypeSymbol
            ' there are no inferred types for non const fields, simply return the type in that case
            If HasDeclaredType Then
                Return Type
            End If

            GetConstantValue(inProgress)

            ' if constantType is nothing it means that there was no initializer given and a diagnostic has already been issued.
            ' In this case we'll return System.Object which is the default type for all locals that could not infer a type.
            Dim constantType = GetInferredConstantType(inProgress)
            Debug.Assert(constantType IsNot Nothing OrElse EqualsValueOrAsNewInitOpt Is Nothing)

            If constantType Is Nothing Then
                constantType = ContainingAssembly.GetSpecialType(SpecialType.System_Object)

            Else
                ' Dev11 only allows a restricted set of types for constant fields (see IsValidTypeForConstField()).
                If Not constantType.IsValidTypeForConstField() Then
                    constantType = ContainingAssembly.GetSpecialType(SpecialType.System_Object)
                Else
                    ' Dev11 translates all inferred Enum constant types into their underlying types
                    constantType = constantType.GetEnumUnderlyingTypeOrSelf
                End If
            End If

            Return constantType
        End Function

        Protected Overridable Function GetInferredConstantType(inProgress As ConstantFieldsInProgress) As TypeSymbol
            Return Nothing
        End Function

        ''' <summary>
        ''' A source field with an explicit initializer. In a declaration declaring multiple fields,
        ''' such as "Dim a, b, c = d", this class is used for the first field only. (Other fields in
        ''' the declaration are instances of SourceFieldSymbolSiblingInitializer.)
        ''' </summary>
        Private Class SourceFieldSymbolWithInitializer
            Inherits SourceMemberFieldSymbol

            ' reference to the initialization syntax of this field,
            ' can be an EqualsValue or AsNew syntax node
            Protected ReadOnly _equalsValueOrAsNewInit As SyntaxReference

            Public Sub New(container As SourceMemberContainerTypeSymbol,
                           syntaxRef As SyntaxReference,
                           name As String,
                           memberFlags As SourceMemberFlags,
                           equalsValueOrAsNewInit As SyntaxReference)
                MyBase.New(container, syntaxRef, name, memberFlags)
                Debug.Assert(equalsValueOrAsNewInit IsNot Nothing)
                Debug.Assert(IsConst = TypeOf Me Is SourceConstFieldSymbolWithInitializer)
                _equalsValueOrAsNewInit = equalsValueOrAsNewInit
            End Sub

            Friend NotOverridable Overrides ReadOnly Property EqualsValueOrAsNewInitOpt As VisualBasicSyntaxNode
                Get
                    Return _equalsValueOrAsNewInit.GetVisualBasicSyntax()
                End Get
            End Property
        End Class

        Private NotInheritable Class SourceConstFieldSymbolWithInitializer
            Inherits SourceFieldSymbolWithInitializer

            ''' <summary>
            ''' A tuple consisting of the evaluated constant value and type
            ''' </summary>
            Private _constantTuple As EvaluatedConstant

            Public Sub New(container As SourceMemberContainerTypeSymbol,
                           syntaxRef As SyntaxReference,
                           name As String,
                           memberFlags As SourceMemberFlags,
                           equalsValueOrAsNewInit As SyntaxReference)
                MyBase.New(container, syntaxRef, name, memberFlags, equalsValueOrAsNewInit)
                Debug.Assert(IsConst)
            End Sub

            Protected Overrides Function GetLazyConstantTuple() As EvaluatedConstant
                Return _constantTuple
            End Function

            Friend Overrides Function GetConstantValue(inProgress As ConstantFieldsInProgress) As ConstantValue
                Return GetConstantValueImpl(inProgress)
            End Function

            Protected Overrides Function MakeConstantTuple(dependencies As ConstantFieldsInProgress.Dependencies, diagnostics As BindingDiagnosticBag) As EvaluatedConstant
                Return ConstantValueUtils.EvaluateFieldConstant(Me, _equalsValueOrAsNewInit, dependencies, diagnostics)
            End Function

            Protected Overrides Sub SetLazyConstantTuple(constantTuple As EvaluatedConstant, diagnostics As BindingDiagnosticBag)
                Debug.Assert(constantTuple IsNot Nothing)
                Dim sourceModule = DirectCast(Me.ContainingModule, SourceModuleSymbol)
                sourceModule.AtomicStoreReferenceAndDiagnostics(_constantTuple, constantTuple, diagnostics)
            End Sub

            Protected Overrides Function GetInferredConstantType(inProgress As ConstantFieldsInProgress) As TypeSymbol
                GetConstantValueImpl(inProgress)

                Dim constantTuple As EvaluatedConstant = GetLazyConstantTuple()
                If constantTuple IsNot Nothing Then
                    Return constantTuple.Type
                End If

                Debug.Assert(Not inProgress.IsEmpty)
                Return New ErrorTypeSymbol()
            End Function
        End Class

        ''' <summary>
        ''' A source field with an explicit initializer. In a declaration declaring multiple
        ''' fields, such as "Dim a, b, c = d", this class is used for the fields other than
        ''' the first. (The first field is an instance of SourceFieldSymbolWithInitializer.)
        ''' An instance of this class holds a reference to the first field in the declaration
        ''' and reuses the bound initializer from that field.
        ''' </summary>
        Private NotInheritable Class SourceFieldSymbolSiblingInitializer
            Inherits SourceMemberFieldSymbol

            ' Sibling field symbol with common initializer (used to
            ' avoid binding constant initializer multiple times).
            Private ReadOnly _sibling As SourceMemberFieldSymbol

            Public Sub New(container As SourceMemberContainerTypeSymbol,
                           syntaxRef As SyntaxReference,
                           name As String,
                           memberFlags As SourceMemberFlags,
                           sibling As SourceMemberFieldSymbol)

                MyBase.New(container, syntaxRef, name, memberFlags)
                _sibling = sibling
            End Sub

            Friend Overrides ReadOnly Property EqualsValueOrAsNewInitOpt As VisualBasicSyntaxNode
                Get
                    Return _sibling.EqualsValueOrAsNewInitOpt
                End Get
            End Property

            Friend Overrides Function GetConstantValue(inProgress As ConstantFieldsInProgress) As ConstantValue
                Return _sibling.GetConstantValue(inProgress)
            End Function

            Protected Overrides Function GetInferredConstantType(inProgress As ConstantFieldsInProgress) As TypeSymbol
                Return _sibling.GetInferredConstantType(inProgress)
            End Function
        End Class

        ' Create variable members, with the given, name, declarator, and declaration syntax.
        Friend Shared Sub Create(container As SourceMemberContainerTypeSymbol,
                                 syntax As FieldDeclarationSyntax,
                                 binder As Binder,
                                 members As SourceNamedTypeSymbol.MembersAndInitializersBuilder,
                                 ByRef staticInitializers As ArrayBuilder(Of FieldOrPropertyInitializer),
                                 ByRef instanceInitializers As ArrayBuilder(Of FieldOrPropertyInitializer),
                                 diagBag As BindingDiagnosticBag)

            Debug.Assert(diagBag.AccumulatesDiagnostics)
            ' Decode the flags.

            Dim validFlags = SourceMemberFlags.AllAccessibilityModifiers Or
                            SourceMemberFlags.Dim Or
                            SourceMemberFlags.Const Or
                            SourceMemberFlags.Shadows Or
                            SourceMemberFlags.Shared

            Dim errorId = ERRID.ERR_BadDimFlags1

            If syntax.Modifiers.Any(SyntaxKind.WithEventsKeyword) Then
                validFlags = validFlags Or SourceMemberFlags.WithEvents
                errorId = ERRID.ERR_BadWithEventsFlags1
            Else
                ' only regular fields can be readonly
                validFlags = validFlags Or SourceMemberFlags.ReadOnly
                errorId = ERRID.ERR_BadDimFlags1
            End If

            Dim modifiers = binder.DecodeModifiers(syntax.Modifiers,
                                                validFlags,
                                                errorId,
                                                If(container.IsValueType, Accessibility.Public, Accessibility.Private),
                                                diagBag.DiagnosticBag)

            If container IsNot Nothing Then
                Select Case container.DeclarationKind
                    Case DeclarationKind.Structure
                        If (modifiers.FoundFlags And SourceMemberFlags.Protected) <> 0 Then
                            binder.ReportModifierError(syntax.Modifiers, ERRID.ERR_StructCantUseVarSpecifier1, diagBag.DiagnosticBag, SyntaxKind.ProtectedKeyword)
                            modifiers = New MemberModifiers(modifiers.FoundFlags And Not SourceMemberFlags.Protected,
                                                            (modifiers.ComputedFlags And Not SourceMemberFlags.AccessibilityMask) Or SourceMemberFlags.AccessibilityPrivate)
                        End If

                        If (modifiers.FoundFlags And SourceMemberFlags.WithEvents) <> 0 Then
                            binder.ReportModifierError(syntax.Modifiers, ERRID.ERR_StructCantUseVarSpecifier1, diagBag.DiagnosticBag, SyntaxKind.WithEventsKeyword)
                            modifiers = New MemberModifiers(modifiers.FoundFlags And Not SourceMemberFlags.WithEvents, modifiers.ComputedFlags)
                        End If

                    Case DeclarationKind.Module
                        ' Member variables in module are implicitly Shared, and cannot be explicitly Shared.
                        If (modifiers.FoundFlags And SourceMemberFlags.InvalidInModule) <> 0 Then
                            binder.ReportModifierError(syntax.Modifiers, ERRID.ERR_ModuleCantUseVariableSpecifier1, diagBag.DiagnosticBag,
                                                       SyntaxKind.SharedKeyword,
                                                       SyntaxKind.ProtectedKeyword,
                                                       SyntaxKind.DefaultKeyword,
                                                       SyntaxKind.MustOverrideKeyword,
                                                       SyntaxKind.OverridableKeyword,
                                                       SyntaxKind.ShadowsKeyword,
                                                       SyntaxKind.OverridesKeyword,
                                                       SyntaxKind.NotOverridableKeyword)
                        End If

                        modifiers = New MemberModifiers(modifiers.FoundFlags And Not SourceMemberFlags.InvalidInModule,
                                                        modifiers.ComputedFlags Or SourceMemberFlags.Shared)

                End Select
            End If

            Const FlagsNotCombinableWithConst As SourceMemberFlags = SourceMemberFlags.WithEvents Or SourceMemberFlags.Shared Or SourceMemberFlags.ReadOnly Or SourceMemberFlags.Dim

            ' Const fields cannot be Shared or ReadOnly or WithEvents
            If (modifiers.FoundFlags And SourceMemberFlags.Const) <> 0 AndAlso
                (modifiers.FoundFlags And FlagsNotCombinableWithConst) <> 0 Then

                If (modifiers.FoundFlags And SourceMemberFlags.Shared) <> 0 Then
                    binder.ReportModifierError(syntax.Modifiers, ERRID.ERR_BadConstFlags1, diagBag.DiagnosticBag, SyntaxKind.SharedKeyword)
                End If

                If (modifiers.FoundFlags And SourceMemberFlags.ReadOnly) <> 0 Then
                    binder.ReportModifierError(syntax.Modifiers, ERRID.ERR_BadConstFlags1, diagBag.DiagnosticBag, SyntaxKind.ReadOnlyKeyword)
                End If

                If (modifiers.FoundFlags And SourceMemberFlags.WithEvents) <> 0 Then
                    binder.ReportModifierError(syntax.Modifiers, ERRID.ERR_BadConstFlags1, diagBag.DiagnosticBag, SyntaxKind.WithEventsKeyword)
                End If

                If (modifiers.FoundFlags And SourceMemberFlags.Dim) <> 0 Then
                    binder.ReportModifierError(syntax.Modifiers, ERRID.ERR_BadConstFlags1, diagBag.DiagnosticBag, SyntaxKind.DimKeyword)
                End If

                modifiers = New MemberModifiers(modifiers.FoundFlags And Not FlagsNotCombinableWithConst,
                                                modifiers.ComputedFlags)
            End If

            ' VB spec 9.5: const fields are implicitly shared
            If (modifiers.FoundFlags And SourceMemberFlags.Const) <> 0 AndAlso
                (modifiers.FoundFlags And SourceMemberFlags.Shared) = 0 Then

                modifiers = New MemberModifiers(modifiers.FoundFlags,
                                                modifiers.ComputedFlags Or SourceMemberFlags.Shared)
            End If

            Dim flags = modifiers.AllFlags

            For Each declarator As VariableDeclaratorSyntax In syntax.Declarators
                ' field declarations can only have one name (strict on & off) if they have an initialization
                If declarator.Names.Count > 1 AndAlso declarator.Initializer IsNot Nothing Then
                    binder.ReportDiagnostic(diagBag, declarator, ERRID.ERR_InitWithMultipleDeclarators)
                End If

                Dim asClauseOpt = declarator.AsClause

                Dim initializerOpt = declarator.Initializer
                Dim initializerSyntax As VisualBasicSyntaxNode = Nothing
                If asClauseOpt IsNot Nothing AndAlso asClauseOpt.Kind = SyntaxKind.AsNewClause Then
                    initializerSyntax = asClauseOpt
                Else
                    initializerSyntax = initializerOpt
                End If

                Dim initializerOptRef = If(initializerSyntax Is Nothing, Nothing, binder.GetSyntaxReference(initializerSyntax))

#If Not ALLOW_STRUCT_INST_INITIALIZERS Then
                'instance members of a structure cannot have initializations (const values are implicitly shared)
                If container.TypeKind = TypeKind.Structure AndAlso
                   (flags And SourceMemberFlags.Shared) = 0 Then
                    If initializerOpt IsNot Nothing Then
                        binder.ReportDiagnostic(diagBag,
                                                If(declarator.Names.Count > 0,
                                                   declarator.Names.Last,
                                                   DirectCast(declarator, VisualBasicSyntaxNode)),
                                                ERRID.ERR_InitializerInStruct)
                    ElseIf asClauseOpt IsNot Nothing AndAlso asClauseOpt.Kind = SyntaxKind.AsNewClause Then
                        binder.ReportDiagnostic(diagBag, DirectCast(asClauseOpt, AsNewClauseSyntax).NewExpression.NewKeyword, ERRID.ERR_SharedStructMemberCannotSpecifyNew)
                    End If
                End If
#End If

                Dim nameCount = declarator.Names.Count
                Dim fieldOrWithEventSymbols(nameCount - 1) As Symbol
                ' even if a declarator has multiple names, treat them all as legal and report issues for all of them
                ' this is what Dev10 does.

                For nameIndex = 0 To nameCount - 1
                    Dim perFieldFlags As SourceMemberFlags = flags
                    If nameIndex = 0 Then
                        perFieldFlags = perFieldFlags Or SourceMemberFlags.FirstFieldDeclarationOfType
                    End If

                    Dim modifiedIdentifier = declarator.Names(nameIndex)
                    Dim identifier = modifiedIdentifier.Identifier
                    Dim omitFurtherDiagnostics As Boolean = String.IsNullOrEmpty(modifiedIdentifier.Identifier.ValueText)

                    If (modifiers.FoundFlags And SourceMemberFlags.Const) <> 0 AndAlso
                        identifier.GetTypeCharacter() = TypeCharacter.None AndAlso
                        modifiedIdentifier.Nullable.Node Is Nothing AndAlso
                        modifiedIdentifier.ArrayBounds Is Nothing AndAlso modifiedIdentifier.ArrayRankSpecifiers.IsEmpty Then
                        ' Fields have inferred type if they have no type character and no As clause, ?, array bounds, or only "As Object" (and the Object must
                        ' be the actual token "Object", not a synonym like "System.Object")
                        Dim simpleAsClauseSyntax = TryCast(asClauseOpt, SimpleAsClauseSyntax)
                        If simpleAsClauseSyntax Is Nothing OrElse
                            (simpleAsClauseSyntax.Type.Kind = SyntaxKind.PredefinedType AndAlso DirectCast(simpleAsClauseSyntax.Type, PredefinedTypeSyntax).Keyword.Kind = SyntaxKind.ObjectKeyword) Then
                            perFieldFlags = perFieldFlags Or SourceMemberFlags.InferredFieldType
                        End If
                    End If

                    Dim modifiedIdentifierRef = binder.GetSyntaxReference(modifiedIdentifier)

                    ' regular field or WithEvents property?
                    If (modifiers.FoundFlags And SourceMemberFlags.WithEvents) = 0 Then
                        Dim fieldSymbol As SourceFieldSymbol

                        If initializerOptRef Is Nothing Then
                            fieldSymbol = New SourceMemberFieldSymbol(
                                container,
                                modifiedIdentifierRef,
                                identifier.ValueText,
                                perFieldFlags)

                        ElseIf nameIndex = 0 Then
                            If (perFieldFlags And SourceMemberFlags.Const) <> 0 Then
                                fieldSymbol = New SourceConstFieldSymbolWithInitializer(
                                    container,
                                    modifiedIdentifierRef,
                                    identifier.ValueText,
                                    perFieldFlags,
                                    initializerOptRef)
                            Else
                                fieldSymbol = New SourceFieldSymbolWithInitializer(
                                    container,
                                    modifiedIdentifierRef,
                                    identifier.ValueText,
                                    perFieldFlags,
                                    initializerOptRef)
                            End If
                        Else
                            fieldSymbol = New SourceFieldSymbolSiblingInitializer(
                                container,
                                modifiedIdentifierRef,
                                identifier.ValueText,
                                perFieldFlags,
                                DirectCast(fieldOrWithEventSymbols(0), SourceMemberFieldSymbol))
                        End If

                        fieldOrWithEventSymbols(nameIndex) = fieldSymbol

                        If syntax.AttributeLists.Count = 0 Then
                            fieldSymbol.SetCustomAttributeData(CustomAttributesBag(Of VisualBasicAttributeData).Empty)
                        End If

                        If (modifiedIdentifier.ArrayBounds IsNot Nothing) AndAlso
                                (modifiedIdentifier.ArrayBounds.Arguments.Count > 0) Then

                            If container.IsStructureType AndAlso Not fieldSymbol.IsShared Then
                                ' Arrays declared as structure members cannot be declared with an initial size.
                                binder.ReportDiagnostic(diagBag, modifiedIdentifier, ERRID.ERR_ArrayInitInStruct)

                            Else
                                If initializerOpt Is Nothing Then
                                    ' Array declaration with implicit initializer.
                                    Dim initializer = Function(precedingInitializersLength As Integer)
                                                          Return New FieldOrPropertyInitializer(fieldSymbol, modifiedIdentifierRef, precedingInitializersLength)
                                                      End Function
                                    If fieldSymbol.IsShared Then
                                        SourceNamedTypeSymbol.AddInitializer(staticInitializers, initializer, members.StaticSyntaxLength)
                                    Else
                                        SourceNamedTypeSymbol.AddInitializer(instanceInitializers, initializer, members.InstanceSyntaxLength)
                                    End If
                                Else
                                    ' Array declaration with implicit and explicit initializers.
                                    binder.ReportDiagnostic(diagBag, modifiedIdentifier, ERRID.ERR_InitWithExplicitArraySizes)
                                End If
                            End If

                        End If

                        container.AddMember(fieldSymbol, binder, members, omitFurtherDiagnostics)
                    Else
                        Dim propertySymbol = SourcePropertySymbol.CreateWithEvents(container,
                                                                                   binder,
                                                                                   identifier,
                                                                                   modifiedIdentifierRef,
                                                                                   modifiers,
                                                                                   nameIndex = 0,
                                                                                   diagBag)

                        fieldOrWithEventSymbols(nameIndex) = propertySymbol

                        container.AddMember(propertySymbol, binder, members, omitFurtherDiagnostics)
                        container.AddMember(propertySymbol.GetMethod, binder, members, omitDiagnostics:=False)
                        container.AddMember(propertySymbol.SetMethod, binder, members, omitDiagnostics:=False)
                        container.AddMember(propertySymbol.AssociatedField, binder, members, omitDiagnostics:=False)
                    End If
                Next

                If initializerOptRef IsNot Nothing Then
                    Dim initializer = Function(precedingInitializersLength As Integer)
                                          Return New FieldOrPropertyInitializer(fieldOrWithEventSymbols.AsImmutableOrNull, initializerOptRef, precedingInitializersLength)
                                      End Function

                    ' all symbols are the same regarding the sharedness
                    Dim symbolsAreShared = nameCount > 0 AndAlso fieldOrWithEventSymbols(0).IsShared

                    If symbolsAreShared Then
                        ' const fields are implicitly shared and get into this list.
                        SourceNamedTypeSymbol.AddInitializer(staticInitializers, initializer, members.StaticSyntaxLength)
                    Else
                        SourceNamedTypeSymbol.AddInitializer(instanceInitializers, initializer, members.InstanceSyntaxLength)
                    End If
                End If
            Next
        End Sub
    End Class
End Namespace
