File: Parser\BlockContexts\DeclarationContext.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 Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
'-----------------------------------------------------------------------------
' Contains the definition of the DeclarationContext
'-----------------------------------------------------------------------------
 
Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax
 
    Friend MustInherit Class DeclarationContext
        Inherits BlockContext
 
        Friend Sub New(kind As SyntaxKind, statement As StatementSyntax, context As BlockContext)
            MyBase.New(kind, statement, context)
        End Sub
 
        Friend Overrides Function Parse() As StatementSyntax
            Return Parser.ParseDeclarationStatement()
        End Function
 
        Friend Overrides Function ProcessSyntax(node As VisualBasicSyntaxNode) As BlockContext
 
            Dim kind As SyntaxKind = node.Kind
            Dim methodBlockKind As SyntaxKind
            Dim methodBase As MethodBaseSyntax
 
            Select Case kind
                Case SyntaxKind.NamespaceStatement
                    ' davidsch - This error check used to be in ParseDeclarations
                    'In dev10 the error was only on the keyword and not the full statement
 
                    Dim reportAnError As Boolean = True
 
                    Dim infos As DiagnosticInfo() = node.GetDiagnostics()
 
                    If infos IsNot Nothing Then
                        For Each info In infos
                            Select Case info.Code
                                Case ERRID.ERR_NamespaceNotAtNamespace, ERRID.ERR_InvInsideEndsProc
                                    reportAnError = False
                                    Exit For
                            End Select
                        Next
                    End If
 
                    If reportAnError Then
                        node = Parser.ReportSyntaxError(node, ERRID.ERR_NamespaceNotAtNamespace)
                    End If
 
                    Dim context = Me.PrevBlock
                    RecoverFromMissingEnd(context)
 
                    'Let the outer context process this statement
                    Return context.ProcessSyntax(node)
 
                Case SyntaxKind.ModuleStatement
                    'In dev10 the error was only on the keyword and not the full statement
                    node = Parser.ReportSyntaxError(node, ERRID.ERR_ModuleNotAtNamespace)
                    Return New TypeBlockContext(SyntaxKind.ModuleBlock, DirectCast(node, StatementSyntax), Me)
 
                Case SyntaxKind.EnumStatement
                    Return New EnumDeclarationBlockContext(DirectCast(node, StatementSyntax), Me)
 
                Case SyntaxKind.ClassStatement
                    Return New TypeBlockContext(SyntaxKind.ClassBlock, DirectCast(node, StatementSyntax), Me)
 
                Case SyntaxKind.StructureStatement
                    Return New TypeBlockContext(SyntaxKind.StructureBlock, DirectCast(node, StatementSyntax), Me)
 
                Case SyntaxKind.InterfaceStatement
                    Return New InterfaceDeclarationBlockContext(DirectCast(node, StatementSyntax), Me)
 
                Case SyntaxKind.SubStatement
                    methodBlockKind = SyntaxKind.SubBlock
                    GoTo HandleMethodBase
 
                Case SyntaxKind.SubNewStatement
                    methodBlockKind = SyntaxKind.ConstructorBlock
                    GoTo HandleMethodBase
 
                Case SyntaxKind.FunctionStatement
                    methodBlockKind = SyntaxKind.FunctionBlock
HandleMethodBase:
                    If Not Parser.IsFirstStatementOnLine(node.GetFirstToken) Then
                        node = Parser.ReportSyntaxError(node, ERRID.ERR_MethodMustBeFirstStatementOnLine)
                    End If
 
                    ' Don't create blocks for MustOverride Sub, Function, and New methods
                    methodBase = DirectCast(node, MethodBaseSyntax)
                    If Not methodBase.Modifiers.Any(SyntaxKind.MustOverrideKeyword) Then
                        Return New MethodBlockContext(methodBlockKind, methodBase, Me)
                    End If
                    Add(node)
 
                Case SyntaxKind.OperatorStatement
                    If Not Parser.IsFirstStatementOnLine(node.GetFirstToken) Then
                        node = Parser.ReportSyntaxError(node, ERRID.ERR_MethodMustBeFirstStatementOnLine)
                    End If
 
                    ' It is a syntax error to have an operator in a module
                    ' This error is reported in declared in Dev10
                    If Me.BlockKind = SyntaxKind.ModuleBlock Then
                        node = Parser.ReportSyntaxError(node, ERRID.ERR_OperatorDeclaredInModule)
                    End If
 
                    Return New MethodBlockContext(SyntaxKind.OperatorBlock, DirectCast(node, StatementSyntax), Me)
 
                Case SyntaxKind.PropertyStatement
                    ' ===== Make an initial attempt at determining if this might be an auto or expanded property
                    ' We can determine which it is in some cases by looking at the specifiers.
                    Dim propertyStatement = DirectCast(node, PropertyStatementSyntax)
                    Dim modifiers = propertyStatement.Modifiers
                    Dim isPropertyBlock As Boolean = False
 
                    If modifiers.Any Then
                        If modifiers.Any(SyntaxKind.MustOverrideKeyword) Then
 
                            ' Mustoverride mean that the property doesn't have any implementation
                            ' This cannot be an auto property so the property may not be initialized.
 
                            ' Check if initializer exists.  Only auto properties may have initialization.
                            node = PropertyBlockContext.ReportErrorIfHasInitializer(DirectCast(node, PropertyStatementSyntax))
 
                            Add(node)
                            Exit Select
 
                        Else
                            isPropertyBlock = modifiers.Any(SyntaxKind.DefaultKeyword,
                                                            SyntaxKind.IteratorKeyword)
                        End If
                    End If
 
                    Return New PropertyBlockContext(propertyStatement, Me, isPropertyBlock)
 
                Case _
                    SyntaxKind.SetAccessorStatement,
                    SyntaxKind.GetAccessorStatement,
                    SyntaxKind.AddHandlerAccessorStatement,
                    SyntaxKind.RemoveHandlerAccessorStatement,
                    SyntaxKind.RaiseEventAccessorStatement
                    'TODO - davidsch - ParseSpecifierDeclaration reports ERRID_ExpectedDeclaration but ParsePropertyOrEventProcedureDefinition reports ERRID_Syntax.
                    '  ERRID_ExpectedDeclaration seems like a better error message. 
 
                    node = Parser.ReportSyntaxError(node, ERRID.ERR_ExpectedDeclaration)
                    Add(node)
 
                Case SyntaxKind.EventStatement
                    Dim eventDecl = DirectCast(node, EventStatementSyntax)
 
                    ' Per Dev10, build a block event only if all the requirements for one are met, 
                    ' i.e. custom modifier and a real AsClause
                    If eventDecl.CustomKeyword IsNot Nothing Then
                        If eventDecl.AsClause.AsKeyword.IsMissing Then
                            ' 'Custom' modifier invalid on event declared without an explicit delegate type.
 
                            ' davidsch - Dev10 put this error on the custom keyword.  
                            node = Parser.ReportSyntaxError(eventDecl, ERRID.ERR_CustomEventRequiresAs)
                        Else
                            Return New EventBlockContext(eventDecl, Me)
                        End If
                    End If
                    Add(node)
 
                Case SyntaxKind.AttributesStatement
                    node = Parser.ReportSyntaxError(node, ERRID.ERR_AttributeStmtWrongOrder)
                    Add(node)
 
                Case SyntaxKind.OptionStatement
                    node = Parser.ReportSyntaxError(node, ERRID.ERR_OptionStmtWrongOrder)
                    Add(node)
 
                Case SyntaxKind.ImportsStatement
                    node = Parser.ReportSyntaxError(node, ERRID.ERR_ImportsMustBeFirst)
                    Add(node)
 
                Case SyntaxKind.InheritsStatement
                    Dim beginStatement = Me.BeginStatement
 
                    If beginStatement IsNot Nothing AndAlso beginStatement.Kind = SyntaxKind.InterfaceStatement Then
                        node = Parser.ReportSyntaxError(node, ERRID.ERR_BadInterfaceOrderOnInherits)
                    Else
                        node = Parser.ReportSyntaxError(node, ERRID.ERR_InheritsStmtWrongOrder)
                    End If
 
                    Add(node)
 
                Case SyntaxKind.ImplementsStatement
                    node = Parser.ReportSyntaxError(node, ERRID.ERR_ImplementsStmtWrongOrder)
                    Add(node)
 
                Case _
                    SyntaxKind.EnumBlock,
                    SyntaxKind.ClassBlock,
                    SyntaxKind.ModuleBlock,
                    SyntaxKind.NamespaceBlock,
                    SyntaxKind.StructureBlock,
                    SyntaxKind.InterfaceBlock,
                    SyntaxKind.SubBlock,
                    SyntaxKind.ConstructorBlock,
                    SyntaxKind.FunctionBlock,
                    SyntaxKind.OperatorBlock,
                    SyntaxKind.PropertyBlock,
                    SyntaxKind.EventBlock
 
                    'TODO - Does parser create Constructors?  Verify that code was ported correctly.
                    ' Handle any block that can be created by this context
                    Add(node)
 
                Case _
                    SyntaxKind.EmptyStatement,
                    SyntaxKind.IncompleteMember,
                    SyntaxKind.FieldDeclaration,
                    SyntaxKind.DelegateSubStatement,
                    SyntaxKind.DelegateFunctionStatement,
                    SyntaxKind.DeclareSubStatement,
                    SyntaxKind.DeclareFunctionStatement,
                    SyntaxKind.EnumMemberDeclaration
 
                    Add(node)
 
                Case SyntaxKind.LabelStatement
                    node = Parser.ReportSyntaxError(node, ERRID.ERR_InvOutsideProc)
                    Add(node)
 
                Case _
                    SyntaxKind.EndStatement,
                    SyntaxKind.StopStatement
                    ' an error has already been reported by ParseGroupEndStatement
                    Add(node)
 
                Case Else
                    ' Do not report an error on End statements - it is reported on the block begin statement 
                    ' or an error that the beginning statement is missing is reported.
                    If Not SyntaxFacts.IsEndBlockLoopOrNextStatement(node.Kind) Then
                        Debug.Assert(IsExecutableStatementOrItsPart(node))
                        node = Parser.ReportSyntaxError(node, ERRID.ERR_ExecutableAsDeclaration)
                    End If
 
                    Add(node)
 
            End Select
 
            Return Me
        End Function
 
        Friend Overrides Function TryLinkSyntax(node As VisualBasicSyntaxNode, ByRef newContext As BlockContext) As LinkResult
            newContext = Nothing
 
            ' block-ending statements are always safe to reuse and are very common
            If KindEndsBlock(node.Kind) Then
                Return UseSyntax(node, newContext)
            End If
 
            Select Case node.Kind
 
                Case _
                    SyntaxKind.NamespaceStatement,
                    SyntaxKind.ModuleStatement,
                    SyntaxKind.EnumStatement,
                    SyntaxKind.ClassStatement,
                    SyntaxKind.StructureStatement,
                    SyntaxKind.InterfaceStatement,
                    SyntaxKind.SubStatement,
                    SyntaxKind.SubNewStatement,
                    SyntaxKind.FunctionStatement,
                    SyntaxKind.OperatorStatement,
                    SyntaxKind.PropertyStatement,
                    SyntaxKind.SetAccessorStatement,
                    SyntaxKind.GetAccessorStatement,
                    SyntaxKind.AddHandlerAccessorStatement,
                    SyntaxKind.RemoveHandlerAccessorStatement,
                    SyntaxKind.RaiseEventAccessorStatement,
                    SyntaxKind.EventStatement,
                    SyntaxKind.AttributesStatement,
                    SyntaxKind.OptionStatement,
                    SyntaxKind.ImportsStatement,
                    SyntaxKind.InheritsStatement,
                    SyntaxKind.ImplementsStatement
 
                    Return UseSyntax(node, newContext)
 
                Case _
                    SyntaxKind.FieldDeclaration,
                    SyntaxKind.DelegateSubStatement,
                    SyntaxKind.DelegateFunctionStatement,
                    SyntaxKind.DeclareSubStatement,
                    SyntaxKind.DeclareFunctionStatement,
                    SyntaxKind.EnumMemberDeclaration
 
                    Return UseSyntax(node, newContext)
 
                Case _
                    SyntaxKind.ClassBlock,
                    SyntaxKind.StructureBlock,
                    SyntaxKind.InterfaceBlock
 
                    Return UseSyntax(node, newContext, DirectCast(node, TypeBlockSyntax).EndBlockStatement.IsMissing)
 
                Case SyntaxKind.EnumBlock
 
                    Return UseSyntax(node, newContext, DirectCast(node, EnumBlockSyntax).EndEnumStatement.IsMissing)
 
                Case _
                    SyntaxKind.SubBlock,
                    SyntaxKind.ConstructorBlock,
                    SyntaxKind.FunctionBlock
                    Return UseSyntax(node, newContext, DirectCast(node, MethodBlockBaseSyntax).End.IsMissing)
 
                Case SyntaxKind.OperatorBlock
                    If Me.BlockKind = SyntaxKind.ModuleBlock Then
                        ' Crumble if this is in a module block for correct error processing
                        newContext = Me
                        Return LinkResult.Crumble
                    End If
                    Return UseSyntax(node, newContext, DirectCast(node, OperatorBlockSyntax).End.IsMissing)
 
                Case SyntaxKind.EventBlock
                    Return UseSyntax(node, newContext, DirectCast(node, EventBlockSyntax).EndEventStatement.IsMissing)
 
                Case SyntaxKind.PropertyBlock
                    Return UseSyntax(node, newContext, DirectCast(node, PropertyBlockSyntax).EndPropertyStatement.IsMissing)
 
                Case _
                    SyntaxKind.NamespaceBlock,
                    SyntaxKind.ModuleBlock
                    ' Crumble to handle errors for NamespaceBlock and ModuleBlock.
                    newContext = Me
                    Return LinkResult.Crumble
 
                ' single line If gets parsed as unexpected If + some garbage
                ' when in declaration context so cannot be reused
                ' see bug 901645 
                Case SyntaxKind.SingleLineIfStatement
                    newContext = Me
                    Return LinkResult.Crumble
 
                Case SyntaxKind.LocalDeclarationStatement
                    ' Crumble so they become field declarations
                    newContext = Me
                    Return LinkResult.Crumble
 
                ' TODO: need to add all executable statements that ParseDeclarationStatement handle as unexpected executables
                ' Move error reporting from ParseDeclarationStatement to the context.
                Case SyntaxKind.IfStatement
                    node = Parser.ReportSyntaxError(node, ERRID.ERR_ExecutableAsDeclaration)
                    Return TryUseStatement(node, newContext)
 
                    ' by default statements are not handled in declaration context
                Case Else
                    newContext = Me
                    Return LinkResult.NotUsed
 
            End Select
        End Function
 
        Friend Overrides Function RecoverFromMismatchedEnd(statement As StatementSyntax) As BlockContext
            Debug.Assert(statement IsNot Nothing)
            ' // The end construct is extraneous. Report an error and leave
            ' // the current context alone.
 
            Dim ErrorId As ERRID = ERRID.ERR_Syntax
            Dim stmtKind = statement.Kind
 
            Select Case (stmtKind)
 
                Case SyntaxKind.EndIfStatement
                    ErrorId = ERRID.ERR_EndIfNoMatchingIf
 
                Case SyntaxKind.EndWithStatement
                    ErrorId = ERRID.ERR_EndWithWithoutWith
 
                Case SyntaxKind.EndSelectStatement
                    ErrorId = ERRID.ERR_EndSelectNoSelect
 
                Case SyntaxKind.EndWhileStatement
                    ErrorId = ERRID.ERR_EndWhileNoWhile
 
                Case SyntaxKind.SimpleLoopStatement, SyntaxKind.LoopWhileStatement, SyntaxKind.LoopUntilStatement
                    ErrorId = ERRID.ERR_LoopNoMatchingDo
 
                Case SyntaxKind.NextStatement
                    ErrorId = ERRID.ERR_NextNoMatchingFor
 
                Case SyntaxKind.EndSubStatement
                    ErrorId = ERRID.ERR_InvalidEndSub
 
                Case SyntaxKind.EndFunctionStatement
                    ErrorId = ERRID.ERR_InvalidEndFunction
 
                Case SyntaxKind.EndOperatorStatement
                    ErrorId = ERRID.ERR_InvalidEndOperator
 
                Case SyntaxKind.EndPropertyStatement
                    ErrorId = ERRID.ERR_InvalidEndProperty
 
                Case SyntaxKind.EndGetStatement
                    ErrorId = ERRID.ERR_InvalidEndGet
 
                Case SyntaxKind.EndSetStatement
                    ErrorId = ERRID.ERR_InvalidEndSet
 
                Case SyntaxKind.EndEventStatement
                    ErrorId = ERRID.ERR_InvalidEndEvent
 
                Case SyntaxKind.EndAddHandlerStatement
                    ErrorId = ERRID.ERR_InvalidEndAddHandler
 
                Case SyntaxKind.EndRemoveHandlerStatement
                    ErrorId = ERRID.ERR_InvalidEndRemoveHandler
 
                Case SyntaxKind.EndRaiseEventStatement
                    ErrorId = ERRID.ERR_InvalidEndRaiseEvent
 
                Case SyntaxKind.EndStructureStatement
                    ErrorId = ERRID.ERR_EndStructureNoStructure
 
                Case SyntaxKind.EndEnumStatement
                    ErrorId = ERRID.ERR_InvalidEndEnum
 
                Case SyntaxKind.EndInterfaceStatement
                    ErrorId = ERRID.ERR_InvalidEndInterface
 
                Case SyntaxKind.EndTryStatement
                    ErrorId = ERRID.ERR_EndTryNoTry
 
                Case SyntaxKind.EndClassStatement
                    ErrorId = ERRID.ERR_EndClassNoClass
 
                Case SyntaxKind.EndModuleStatement
                    ErrorId = ERRID.ERR_EndModuleNoModule
 
                Case SyntaxKind.EndNamespaceStatement
                    ErrorId = ERRID.ERR_EndNamespaceNoNamespace
 
                Case SyntaxKind.EndUsingStatement
                    ErrorId = ERRID.ERR_EndUsingWithoutUsing
 
                Case SyntaxKind.EndSyncLockStatement
                    ErrorId = ERRID.ERR_EndSyncLockNoSyncLock
 
#If UNDONE Then
                        'TODO - davidsch - handle regions
                    Case NodeKind.EndRegionStatement
                        ErrorId = ERRID.ERR_EndRegionNoRegion
#End If
 
                Case SyntaxKind.EmptyStatement, SyntaxKind.IncompleteMember
                    ErrorId = ERRID.ERR_UnrecognizedEnd
 
                Case Else
                    Throw New ArgumentException("Statement must be an end block statement")
 
            End Select
 
            'TODO 
            '  Should ReportSyntaxError be on the Parser or can it be made static
            ' If the parser isn't needed for more than this then remove it from the context.
            statement = Parser.ReportSyntaxError(statement, ErrorId)
            Return ProcessSyntax(statement)
        End Function
 
        Friend Overrides Function EndBlock(endStmt As StatementSyntax) As BlockContext
            Dim blockSyntax = CreateBlockSyntax(endStmt)
            Dim context = PrevBlock.ProcessSyntax(blockSyntax)
            Return context
        End Function
 
        Friend Overrides Function ProcessStatementTerminator(lambdaContext As BlockContext) As BlockContext
            Parser.ConsumeStatementTerminator(colonAsSeparator:=False)
            Return Me
        End Function
 
        Friend Overrides ReadOnly Property IsSingleLine As Boolean
            Get
                Return False
            End Get
        End Property
 
    End Class
 
End Namespace