File: Parser\BlockContexts\BlockContext.vb
Web Access
Project: src\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.

'-----------------------------------------------------------------------------
' Contains the definition of the BlockContext
'-----------------------------------------------------------------------------
Imports Microsoft.CodeAnalysis.Syntax.InternalSyntax
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Imports InternalSyntaxFactory = Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax.SyntaxFactory

Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax

    Friend MustInherit Class BlockContext
        Implements ISyntaxFactoryContext

        Private _beginStatement As StatementSyntax

        Protected _parser As Parser
        Protected _statements As SyntaxListBuilder(Of StatementSyntax)

        Private ReadOnly _kind As SyntaxKind
        Private ReadOnly _endKind As SyntaxKind
        Private ReadOnly _prev As BlockContext
        Private ReadOnly _isWithinMultiLineLambda As Boolean
        Private ReadOnly _isWithinSingleLineLambda As Boolean
        Private ReadOnly _isWithinAsyncMethodOrLambda As Boolean
        Private ReadOnly _isWithinIteratorMethodOrLambdaOrProperty As Boolean
        Private ReadOnly _level As Integer
        Private ReadOnly _syntaxFactory As ContextAwareSyntaxFactory

        Protected Sub New(kind As SyntaxKind, statement As StatementSyntax, prev As BlockContext)
            _beginStatement = statement
            _kind = kind
            _prev = prev
            _syntaxFactory = New ContextAwareSyntaxFactory(Me)

            If prev IsNot Nothing Then
                _isWithinSingleLineLambda = prev._isWithinSingleLineLambda
                _isWithinMultiLineLambda = prev._isWithinMultiLineLambda
            End If

            If Not _isWithinSingleLineLambda Then
                _isWithinSingleLineLambda = SyntaxFacts.IsSingleLineLambdaExpression(_kind)
            End If

            If Not _isWithinMultiLineLambda Then
                _isWithinMultiLineLambda = SyntaxFacts.IsMultiLineLambdaExpression(_kind)
            End If

            Select Case _kind

                Case SyntaxKind.PropertyBlock

                    _isWithinIteratorMethodOrLambdaOrProperty = DirectCast(statement, PropertyStatementSyntax).Modifiers.Any(SyntaxKind.IteratorKeyword)

                Case SyntaxKind.GetAccessorBlock,
                     SyntaxKind.SetAccessorBlock

                    Debug.Assert(_prev IsNot Nothing)
                    _isWithinIteratorMethodOrLambdaOrProperty = _prev.IsWithinIteratorMethodOrLambdaOrProperty

                Case SyntaxKind.SubBlock,
                     SyntaxKind.FunctionBlock

                    _isWithinAsyncMethodOrLambda = DirectCast(statement, MethodStatementSyntax).Modifiers.Any(SyntaxKind.AsyncKeyword)
                    _isWithinIteratorMethodOrLambdaOrProperty = DirectCast(statement, MethodStatementSyntax).Modifiers.Any(SyntaxKind.IteratorKeyword)

                Case SyntaxKind.SingleLineSubLambdaExpression,
                        SyntaxKind.MultiLineSubLambdaExpression,
                        SyntaxKind.SingleLineFunctionLambdaExpression,
                        SyntaxKind.MultiLineFunctionLambdaExpression

                    _isWithinAsyncMethodOrLambda = DirectCast(statement, LambdaHeaderSyntax).Modifiers.Any(SyntaxKind.AsyncKeyword)
                    _isWithinIteratorMethodOrLambdaOrProperty = DirectCast(statement, LambdaHeaderSyntax).Modifiers.Any(SyntaxKind.IteratorKeyword)

                Case Else

                    If _prev IsNot Nothing Then
                        _isWithinAsyncMethodOrLambda = _prev.IsWithinAsyncMethodOrLambda
                        _isWithinIteratorMethodOrLambdaOrProperty = _prev.IsWithinIteratorMethodOrLambdaOrProperty
                    End If
            End Select

            _endKind = GetEndKind(kind)
            _level = If(prev IsNot Nothing, prev.Level + 1, 0)

            If prev IsNot Nothing Then
                _parser = prev.Parser
                _statements = _parser._pool.Allocate(Of StatementSyntax)()
            End If
        End Sub

        Friend ReadOnly Property BeginStatement As StatementSyntax
            Get
                Return _beginStatement
            End Get
        End Property

        Friend Sub GetBeginEndStatements(Of T1 As StatementSyntax, T2 As StatementSyntax)(ByRef beginStmt As T1, ByRef endStmt As T2)
            Debug.Assert(BeginStatement IsNot Nothing)

            beginStmt = DirectCast(BeginStatement, T1)

            If endStmt Is Nothing Then
                Dim errorId As ERRID
                endStmt = DirectCast(CreateMissingEnd(errorId), T2)

                If errorId <> Nothing Then
                    beginStmt = Parser.ReportSyntaxError(beginStmt, errorId)
                End If
            End If
        End Sub

        Friend Overridable Function KindEndsBlock(kind As SyntaxKind) As Boolean
            Return _endKind = kind
        End Function

        Friend ReadOnly Property IsLineIf As Boolean
            Get
                Return _kind = SyntaxKind.SingleLineIfStatement OrElse _kind = SyntaxKind.SingleLineElseClause
            End Get
        End Property

        Friend ReadOnly Property IsWithinLambda As Boolean
            Get
                Return _isWithinMultiLineLambda Or _isWithinSingleLineLambda
            End Get
        End Property

        Friend ReadOnly Property IsWithinSingleLineLambda As Boolean
            Get
                Return _isWithinSingleLineLambda
            End Get
        End Property

        Friend Overridable ReadOnly Property IsWithinAsyncMethodOrLambda As Boolean Implements ISyntaxFactoryContext.IsWithinAsyncMethodOrLambda
            Get
                Return _isWithinAsyncMethodOrLambda
            End Get
        End Property

        Friend Overridable ReadOnly Property IsWithinIteratorContext As Boolean Implements ISyntaxFactoryContext.IsWithinIteratorContext
            Get
                Return _isWithinIteratorMethodOrLambdaOrProperty
            End Get
        End Property

        Friend ReadOnly Property IsWithinIteratorMethodOrLambdaOrProperty As Boolean
            Get
                Return _isWithinIteratorMethodOrLambdaOrProperty
            End Get
        End Property

        'TODO - Remove dependency on Parser
        '  For errors call error function directly
        '  for parsing, just pass a delegate to the context
        Friend Property Parser As Parser
            Get
                Return _parser
            End Get
            Set(value As Parser)
                Debug.Assert(BlockKind = SyntaxKind.CompilationUnit)
                _parser = value
            End Set
        End Property

        Friend ReadOnly Property SyntaxFactory As ContextAwareSyntaxFactory
            Get
                Return _syntaxFactory
            End Get
        End Property

        Friend ReadOnly Property BlockKind As SyntaxKind
            Get
                Return _kind
            End Get
        End Property

        Friend ReadOnly Property PrevBlock As BlockContext
            Get
                Return _prev
            End Get
        End Property

        Friend ReadOnly Property Level As Integer
            Get
                Return _level
            End Get
        End Property

        Friend Sub Add(node As VisualBasicSyntaxNode)
            Debug.Assert(node IsNot Nothing)

            _statements.Add(DirectCast(node, StatementSyntax))
        End Sub

        Friend ReadOnly Property Statements As SyntaxListBuilder(Of StatementSyntax)
            Get
                Return _statements
            End Get
        End Property

        Friend Sub FreeStatements()
            _parser._pool.Free(_statements)
        End Sub

        Friend Function Body() As CodeAnalysis.Syntax.InternalSyntax.SyntaxList(Of StatementSyntax)
            Dim result = _statements.ToList()

            _statements.Clear()

            Return result
        End Function

        ''' <summary>
        ''' Returns the statement if there is exactly one in the body,
        ''' otherwise returns Nothing.
        ''' </summary>
        Friend Function SingleStatementOrDefault() As StatementSyntax
            Return If(_statements.Count = 1, _statements(0), Nothing)
        End Function

        ''' <summary>
        ''' Return an empty body if the body is a single, zero-width EmptyStatement,
        ''' otherwise returns the entire body.
        ''' </summary>
        Friend Function OptionalBody() As CodeAnalysis.Syntax.InternalSyntax.SyntaxList(Of StatementSyntax)
            Dim statement = SingleStatementOrDefault()

            If statement IsNot Nothing AndAlso
                statement.Kind = SyntaxKind.EmptyStatement AndAlso
                statement.FullWidth = 0 Then
                Return Nothing
            End If

            Return Body()
        End Function

        Friend Function Body(Of T As StatementSyntax)() As CodeAnalysis.Syntax.InternalSyntax.SyntaxList(Of T)
            Dim result = _statements.ToList(Of T)()

            _statements.Clear()

            Return result
        End Function

        ' Same as Body(), but use a SyntaxListWithManyChildren if the
        ' body is large enough, so we get red node with weak children.
        Friend Function BodyWithWeakChildren() As CodeAnalysis.Syntax.InternalSyntax.SyntaxList(Of StatementSyntax)
            If IsLargeEnoughNonEmptyStatementList(_statements) Then
                Dim result = New CodeAnalysis.Syntax.InternalSyntax.SyntaxList(Of StatementSyntax)(
                    CodeAnalysis.Syntax.InternalSyntax.SyntaxList.List(CType(_statements, SyntaxListBuilder).ToArray))

                _statements.Clear()

                Return result
            Else
                Return Body()
            End If
        End Function

        ' Is this statement list non-empty, and large enough to make using weak children beneficial?
        Private Shared Function IsLargeEnoughNonEmptyStatementList(statements As SyntaxListBuilder(Of StatementSyntax)) As Boolean
            If statements.Count = 0 Then
                Return False
            ElseIf statements.Count <= 2 Then
                ' If we have a single statement (Count include separators), it might be small, like "return null", or large,
                ' like a loop or if or switch with many statements inside. Use the width as a proxy for
                ' how big it is. If it's small, its better to forgo a many children list anyway, since the
                ' weak reference would consume as much memory as is saved.
                Return statements(0).Width > 60
            Else
                ' For 2 or more statements, go ahead and create a many-children lists.
                Return True
            End If
        End Function

        Friend Function BaseDeclarations(Of T As InheritsOrImplementsStatementSyntax)() As CodeAnalysis.Syntax.InternalSyntax.SyntaxList(Of T)

            Dim result = _statements.ToList(Of T)()

            _statements.Clear()
            Return result
        End Function

        Friend MustOverride Function Parse() As StatementSyntax

        Friend MustOverride Function ProcessSyntax(syntax As VisualBasicSyntaxNode) As BlockContext

        Friend MustOverride Function CreateBlockSyntax(statement As StatementSyntax) As VisualBasicSyntaxNode

        Friend MustOverride Function EndBlock(statement As StatementSyntax) As BlockContext

        Friend MustOverride Function RecoverFromMismatchedEnd(statement As StatementSyntax) As BlockContext

        Friend Overridable Function ResyncAndProcessStatementTerminator(statement As StatementSyntax, lambdaContext As BlockContext) As BlockContext
            Dim unexpected = Parser.ResyncAt()
            HandleAnyUnexpectedTokens(statement, unexpected)
            Return ProcessStatementTerminator(lambdaContext)
        End Function

        Friend MustOverride Function ProcessStatementTerminator(lambdaContext As BlockContext) As BlockContext

        Friend MustOverride ReadOnly Property IsSingleLine As Boolean

        Friend Overridable ReadOnly Property IsLambda As Boolean
            Get
                Return False
            End Get
        End Property

        Private Sub HandleAnyUnexpectedTokens(currentStmt As StatementSyntax, unexpected As CodeAnalysis.Syntax.InternalSyntax.SyntaxList(Of SyntaxToken))
            If unexpected.Node Is Nothing Then
                Return
            End If

            Dim index As Integer
            Dim stmt As StatementSyntax

            If _statements.Count = 0 Then
                index = -1
                stmt = _beginStatement
            Else
                index = _statements.Count - 1
                stmt = _statements(index)
            End If

            Debug.Assert(stmt IsNot Nothing)

            If Not currentStmt.ContainsDiagnostics AndAlso Not unexpected.ContainsDiagnostics Then
                stmt = stmt.AddTrailingSyntax(unexpected, ERRID.ERR_ExpectedEOS)
            Else
                ' Don't report ERRID_ExpectedEOS when the statement is known to be bad
                stmt = stmt.AddTrailingSyntax(unexpected)
            End If

            If index = -1 Then
                _beginStatement = stmt
            Else
                _statements(index) = stmt
            End If
        End Sub

        <Flags()>
        Friend Enum LinkResult
            NotUsed = 0             ' The syntax cannot be used.  Force a reparse.
            Used = 1                ' Reuse the syntax.
            SkipTerminator = 2      ' Syntax is not followed by a statement terminator.
            MissingTerminator = 4   ' Statement terminator is missing.
            TerminatorFlags = 6     ' Combination of the above 2 flags.
            Crumble = 8             ' Crumble the syntax and try to reuse the parts.
        End Enum

        Friend MustOverride Function TryLinkSyntax(node As VisualBasicSyntaxNode, ByRef newContext As BlockContext) As LinkResult

        Friend Function LinkSyntax(node As VisualBasicSyntaxNode) As BlockContext
            Debug.Assert(node IsNot Nothing)

            Dim kind As SyntaxKind = node.Kind
            Dim context = Me

            While context IsNot Nothing
                If context.KindEndsBlock(kind) Then
                    ' Note, end statements in single line lambdas and single line if's can never close an outer context.
                    Dim scope = FindNearestLambdaOrSingleLineIf(context)
                    If scope IsNot Nothing Then
                        If scope.IsLambda Then
                            ' Don't allow end statements from outer blocks to terminate single line statement lambdas.
                            ' Single line if's have a special error for this case but single line lambdas don't.
                            Exit While
                        Else
                            ' Don't allow end statements from outer blocks to terminate single line ifs.
                            node = Parser.ReportSyntaxError(node, ERRID.ERR_BogusWithinLineIf)
                            Return ProcessSyntax(node)
                            Debug.Assert(scope.IsLineIf)
                        End If
                    Else
                        If context IsNot Me Then
                            'This statement ends a block higher up.
                            'End all blocks from Me up to this one with a missing ends.
                            RecoverFromMissingEnd(context)
                        End If
                        'Add the block to the context above
                        Return context.EndBlock(DirectCast(node, StatementSyntax))
                    End If
                ElseIf SyntaxFacts.IsEndBlockLoopOrNextStatement(kind) Then
                    ' See if this kind closes an enclosing statement context
                    context = context.PrevBlock
                Else
                    Return ProcessSyntax(node)
                End If
            End While

            'No match was found for the end block statement
            'Add it to the current context and leave the context unchanged
            Return RecoverFromMismatchedEnd(DirectCast(node, StatementSyntax))
        End Function

        Friend Function UseSyntax(node As VisualBasicSyntaxNode, ByRef newContext As BlockContext, Optional AddMissingTerminator As Boolean = False) As LinkResult
            ' get off the current node as we are definitely using it and LinkStatement may need to look at next token
            Parser.GetNextSyntaxNode()

            ' TODO: this will add an error to the statement. Perhaps duplicating it
            ' context-sensitive errors should be filtered out before re-using nodes.
            ' or better we should put contextual errors on the actual block not on the offending node (if possible).
            newContext = LinkSyntax(node)

            If AddMissingTerminator Then
                Return LinkResult.Used Or LinkResult.MissingTerminator
            End If

            Return LinkResult.Used
        End Function

        Friend Function TryUseStatement(node As VisualBasicSyntaxNode, ByRef newContext As BlockContext) As LinkResult
            Dim statement = TryCast(node, StatementSyntax)
            If statement IsNot Nothing Then
                ' get off the current node as we are definitely using it and LinkStatement may need to look at next token
                Return UseSyntax(statement, newContext)
            Else
                Return LinkResult.NotUsed
            End If
        End Function

        ' Returns Nothing if the statement isn't processed
        Friend Function TryProcessExecutableStatement(node As VisualBasicSyntaxNode) As BlockContext
            ' top-level statements
            Select Case node.Kind
                Case SyntaxKind.SingleLineIfStatement
                    Add(node)

                Case SyntaxKind.IfStatement
                    ' A single line if has a "then" on the line and is not followed by a ":", EOL or EOF.
                    ' It is OK for else to follow a single line if. i.e
                    '       "if true then if true then else else
                    Dim ifStmt = DirectCast(node, IfStatementSyntax)
                    If ifStmt.ThenKeyword IsNot Nothing AndAlso Not SyntaxFacts.IsTerminator(Parser.CurrentToken.Kind) Then
                        Return New SingleLineIfBlockContext(ifStmt, Me)
                    Else
                        Return New IfBlockContext(ifStmt, Me)
                    End If

                Case SyntaxKind.ElseStatement
                    ' davidsch
                    ' This error used to be reported in ParseStatementInMethodBody.  Move to context.
                    ' It used to be this error with a note that Semantics doesn't like an ELSEIF without an IF.
                    ' Fully parse for now.
                    ' ReportUnrecognizedStatementError(ERRID_ElseIfNoMatchingIf, ErrorInConstruct)

                    Add(Parser.ReportSyntaxError(node, ERRID.ERR_ElseNoMatchingIf))

                Case SyntaxKind.ElseIfStatement
                    ' davidsch
                    ' This error used to be reported in ParseStatementInMethodBody.  Move to context.
                    ' It used to be this error with a note that Semantics doesn't like an ELSEIF without an IF.
                    ' Fully parse for now.
                    ' ReportUnrecognizedStatementError(ERRID_ElseIfNoMatchingIf, ErrorInConstruct)

                    Add(Parser.ReportSyntaxError(node, ERRID.ERR_ElseIfNoMatchingIf))

                Case SyntaxKind.SimpleDoStatement,
                     SyntaxKind.DoWhileStatement,
                     SyntaxKind.DoUntilStatement
                    Return New DoLoopBlockContext(DirectCast(node, StatementSyntax), Me)

                Case SyntaxKind.ForStatement, SyntaxKind.ForEachStatement
                    Return New ForBlockContext(DirectCast(node, StatementSyntax), Me)

                Case SyntaxKind.SelectStatement
                    Return New SelectBlockContext(DirectCast(node, StatementSyntax), Me)

                Case SyntaxKind.CaseStatement
                    'TODO - davidsch
                    ' In dev10 the error is reported on the CASE not the statement.  If needed this error can be
                    ' moved to ParseCaseStatement.
                    Add(Parser.ReportSyntaxError(node, ERRID.ERR_CaseNoSelect))

                Case SyntaxKind.CaseElseStatement
                    'TODO - davidsch
                    ' In dev10 the error is reported on the CASE not the statement.  If needed this error can be
                    ' moved to ParseCaseStatement.
                    Add(Parser.ReportSyntaxError(node, ERRID.ERR_CaseElseNoSelect))

                Case SyntaxKind.WhileStatement
                    Return New StatementBlockContext(SyntaxKind.WhileBlock, DirectCast(node, StatementSyntax), Me)

                Case SyntaxKind.WithStatement
                    Return New StatementBlockContext(SyntaxKind.WithBlock, DirectCast(node, StatementSyntax), Me)

                Case SyntaxKind.SyncLockStatement
                    Return New StatementBlockContext(SyntaxKind.SyncLockBlock, DirectCast(node, StatementSyntax), Me)

                Case SyntaxKind.UsingStatement
                    Return New StatementBlockContext(SyntaxKind.UsingBlock, DirectCast(node, StatementSyntax), Me)

                Case SyntaxKind.TryStatement
                    Return New TryBlockContext(DirectCast(node, StatementSyntax), Me)

                Case SyntaxKind.CatchStatement, SyntaxKind.FinallyStatement
                    Dim context = FindNearestInSameMethodScope(SyntaxKind.TryBlock, SyntaxKind.CatchBlock, SyntaxKind.FinallyBlock)
                    If context IsNot Nothing Then
                        RecoverFromMissingEnd(context)
                        Return context.ProcessSyntax(DirectCast(node, StatementSyntax))
                    End If

                    ' In dev10 the error is reported on the CATCH not the statement.
                    ' If needed this error can be moved to ParseCatchStatement.
                    Add(Parser.ReportSyntaxError(node, If(node.Kind = SyntaxKind.CatchStatement, ERRID.ERR_CatchNoMatchingTry, ERRID.ERR_FinallyNoMatchingTry)))

                Case SyntaxKind.SelectBlock,
                     SyntaxKind.WhileBlock,
                     SyntaxKind.WithBlock,
                     SyntaxKind.SyncLockBlock,
                     SyntaxKind.UsingBlock,
                     SyntaxKind.TryBlock,
                     SyntaxKind.SimpleDoLoopBlock,
                     SyntaxKind.DoWhileLoopBlock,
                     SyntaxKind.DoUntilLoopBlock,
                     SyntaxKind.DoLoopWhileBlock,
                     SyntaxKind.DoLoopUntilBlock,
                     SyntaxKind.ForBlock,
                     SyntaxKind.ForEachBlock,
                     SyntaxKind.SingleLineIfStatement,
                     SyntaxKind.MultiLineIfBlock
                    ' Handle any block that can be created by this context
                    Add(node)

                Case Else
                    If Not TypeOf node Is ExecutableStatementSyntax Then
                        Return Nothing
                    End If

                    Add(node)
            End Select
            Return Me
        End Function

        Friend Function TryLinkStatement(node As VisualBasicSyntaxNode, ByRef newContext As BlockContext) As LinkResult
            newContext = Nothing
            Select Case node.Kind
                Case SyntaxKind.SelectBlock
                    Return UseSyntax(node, newContext, DirectCast(node, SelectBlockSyntax).EndSelectStatement.IsMissing)

                Case SyntaxKind.WhileBlock
                    Return UseSyntax(node, newContext, DirectCast(node, WhileBlockSyntax).EndWhileStatement.IsMissing)

                Case SyntaxKind.WithBlock
                    Return UseSyntax(node, newContext, DirectCast(node, WithBlockSyntax).EndWithStatement.IsMissing)

                Case SyntaxKind.SyncLockBlock
                    Return UseSyntax(node, newContext, DirectCast(node, SyncLockBlockSyntax).EndSyncLockStatement.IsMissing)

                Case SyntaxKind.UsingBlock
                    Return UseSyntax(node, newContext, DirectCast(node, UsingBlockSyntax).EndUsingStatement.IsMissing)

                Case SyntaxKind.TryBlock
                    Return UseSyntax(node, newContext, DirectCast(node, TryBlockSyntax).EndTryStatement.IsMissing)

                Case SyntaxKind.SimpleDoLoopBlock,
                     SyntaxKind.DoWhileLoopBlock,
                     SyntaxKind.DoUntilLoopBlock,
                     SyntaxKind.DoLoopWhileBlock,
                     SyntaxKind.DoLoopUntilBlock

                    Return UseSyntax(node, newContext, DirectCast(node, DoLoopBlockSyntax).LoopStatement.IsMissing)

                Case SyntaxKind.ForBlock,
                     SyntaxKind.ForEachBlock

                    ' The EndOpt syntax can influence the next context in case it contains
                    ' several control variables. If they are still valid needs to be checked in the ForBlockContext.
                    ' This is the reason why we can't simply reuse the syntax here. 
                    newContext = Me
                    Return LinkResult.Crumble

                Case SyntaxKind.SingleLineIfStatement
                    Return UseSyntax(node, newContext)

                Case SyntaxKind.MultiLineIfBlock
                    Return UseSyntax(node, newContext, DirectCast(node, MultiLineIfBlockSyntax).EndIfStatement.IsMissing)

                Case SyntaxKind.NextStatement
                    ' Don't reuse a next statement. The parser matches the variable list with the for context blocks.
                    ' In order to reuse the next statement that error checking needs to be moved from the parser to the
                    ' contexts.  For now, crumble and reparse.  The next statement is small and fast to parse.
                    newContext = Me
                    Return LinkResult.NotUsed

                Case Else
                    Return TryUseStatement(node, newContext)

            End Select
        End Function

        Private Function CreateMissingEnd(ByRef errorId As ERRID) As StatementSyntax
            Return CreateMissingEnd(BlockKind, errorId)
        End Function

        Private Function CreateMissingEnd(kind As SyntaxKind, ByRef errorId As ERRID) As StatementSyntax
            Dim endStmt As StatementSyntax
            Dim missingEndKeyword = InternalSyntaxFactory.MissingKeyword(SyntaxKind.EndKeyword)

            Select Case kind
                Case SyntaxKind.NamespaceBlock
                    endStmt = SyntaxFactory.EndNamespaceStatement(missingEndKeyword, InternalSyntaxFactory.MissingKeyword(SyntaxKind.NamespaceKeyword))
                    errorId = ERRID.ERR_ExpectedEndNamespace

                Case SyntaxKind.ModuleBlock
                    endStmt = SyntaxFactory.EndModuleStatement(missingEndKeyword, InternalSyntaxFactory.MissingKeyword(SyntaxKind.ModuleKeyword))
                    errorId = ERRID.ERR_ExpectedEndModule

                Case SyntaxKind.ClassBlock
                    endStmt = SyntaxFactory.EndClassStatement(missingEndKeyword, InternalSyntaxFactory.MissingKeyword(SyntaxKind.ClassKeyword))
                    errorId = ERRID.ERR_ExpectedEndClass

                Case SyntaxKind.StructureBlock
                    endStmt = SyntaxFactory.EndStructureStatement(missingEndKeyword, InternalSyntaxFactory.MissingKeyword(SyntaxKind.StructureKeyword))
                    errorId = ERRID.ERR_ExpectedEndStructure

                Case SyntaxKind.InterfaceBlock
                    endStmt = SyntaxFactory.EndInterfaceStatement(missingEndKeyword, InternalSyntaxFactory.MissingKeyword(SyntaxKind.InterfaceKeyword))
                    errorId = ERRID.ERR_MissingEndInterface

                Case SyntaxKind.EnumBlock
                    endStmt = SyntaxFactory.EndEnumStatement(missingEndKeyword, InternalSyntaxFactory.MissingKeyword(SyntaxKind.EnumKeyword))
                    errorId = ERRID.ERR_MissingEndEnum

                Case SyntaxKind.SubBlock,
                    SyntaxKind.ConstructorBlock
                    endStmt = SyntaxFactory.EndSubStatement(missingEndKeyword, InternalSyntaxFactory.MissingKeyword(SyntaxKind.SubKeyword))
                    'TODO - davidsch make these expected error message names consistent. Some are EndXXExpected and others are ExpectedEndXX
                    errorId = ERRID.ERR_EndSubExpected

                Case SyntaxKind.MultiLineSubLambdaExpression
                    endStmt = SyntaxFactory.EndSubStatement(missingEndKeyword, InternalSyntaxFactory.MissingKeyword(SyntaxKind.SubKeyword))
                    errorId = ERRID.ERR_MultilineLambdaMissingSub

                Case SyntaxKind.FunctionBlock
                    endStmt = SyntaxFactory.EndFunctionStatement(missingEndKeyword, InternalSyntaxFactory.MissingKeyword(SyntaxKind.FunctionKeyword))
                    errorId = ERRID.ERR_EndFunctionExpected

                Case SyntaxKind.MultiLineFunctionLambdaExpression
                    endStmt = SyntaxFactory.EndFunctionStatement(missingEndKeyword, InternalSyntaxFactory.MissingKeyword(SyntaxKind.FunctionKeyword))
                    errorId = ERRID.ERR_MultilineLambdaMissingFunction

                Case SyntaxKind.OperatorBlock
                    endStmt = SyntaxFactory.EndOperatorStatement(missingEndKeyword, InternalSyntaxFactory.MissingKeyword(SyntaxKind.OperatorKeyword))
                    errorId = ERRID.ERR_EndOperatorExpected

                Case SyntaxKind.PropertyBlock
                    endStmt = SyntaxFactory.EndPropertyStatement(missingEndKeyword, InternalSyntaxFactory.MissingKeyword(SyntaxKind.PropertyKeyword))
                    'TODO rename this enum for consistency ERRID_MissingEndProperty
                    errorId = ERRID.ERR_EndProp

                Case SyntaxKind.GetAccessorBlock
                    endStmt = SyntaxFactory.EndGetStatement(missingEndKeyword, InternalSyntaxFactory.MissingKeyword(SyntaxKind.GetKeyword))
                    errorId = ERRID.ERR_MissingEndGet

                Case SyntaxKind.SetAccessorBlock
                    endStmt = SyntaxFactory.EndSetStatement(missingEndKeyword, InternalSyntaxFactory.MissingKeyword(SyntaxKind.SetKeyword))
                    errorId = ERRID.ERR_MissingEndSet

                Case SyntaxKind.EventBlock
                    endStmt = SyntaxFactory.EndEventStatement(missingEndKeyword, InternalSyntaxFactory.MissingKeyword(SyntaxKind.EventKeyword))
                    'TODO rename this enum for consistency ERRID_MissingEndProperty
                    errorId = ERRID.ERR_MissingEndEvent

                Case SyntaxKind.AddHandlerAccessorBlock
                    endStmt = SyntaxFactory.EndAddHandlerStatement(missingEndKeyword, InternalSyntaxFactory.MissingKeyword(SyntaxKind.AddHandlerKeyword))
                    errorId = ERRID.ERR_MissingEndAddHandler

                Case SyntaxKind.RemoveHandlerAccessorBlock
                    endStmt = SyntaxFactory.EndRemoveHandlerStatement(missingEndKeyword, InternalSyntaxFactory.MissingKeyword(SyntaxKind.RemoveHandlerKeyword))
                    errorId = ERRID.ERR_MissingEndRemoveHandler

                Case SyntaxKind.RaiseEventAccessorBlock
                    endStmt = SyntaxFactory.EndRaiseEventStatement(missingEndKeyword, InternalSyntaxFactory.MissingKeyword(SyntaxKind.RaiseEventKeyword))
                    errorId = ERRID.ERR_MissingEndRaiseEvent

                Case SyntaxKind.MultiLineIfBlock, SyntaxKind.ElseIfBlock, SyntaxKind.ElseBlock
                    endStmt = SyntaxFactory.EndIfStatement(missingEndKeyword, InternalSyntaxFactory.MissingKeyword(SyntaxKind.IfKeyword))
                    errorId = ERRID.ERR_ExpectedEndIf

                Case SyntaxKind.SimpleDoLoopBlock, SyntaxKind.DoWhileLoopBlock
                    endStmt = SyntaxFactory.SimpleLoopStatement(InternalSyntaxFactory.MissingKeyword(SyntaxKind.LoopKeyword), Nothing)
                    errorId = ERRID.ERR_ExpectedLoop

                Case SyntaxKind.WhileBlock
                    endStmt = SyntaxFactory.EndWhileStatement(missingEndKeyword, InternalSyntaxFactory.MissingKeyword(SyntaxKind.WhileKeyword))
                    errorId = ERRID.ERR_ExpectedEndWhile

                Case SyntaxKind.WithBlock
                    endStmt = SyntaxFactory.EndWithStatement(missingEndKeyword, InternalSyntaxFactory.MissingKeyword(SyntaxKind.WithKeyword))
                    errorId = ERRID.ERR_ExpectedEndWith

                Case SyntaxKind.ForBlock, SyntaxKind.ForEachBlock
                    endStmt = SyntaxFactory.NextStatement(InternalSyntaxFactory.MissingKeyword(SyntaxKind.NextKeyword), Nothing)
                    errorId = ERRID.ERR_ExpectedNext

                Case SyntaxKind.SyncLockBlock
                    endStmt = SyntaxFactory.EndSyncLockStatement(missingEndKeyword, InternalSyntaxFactory.MissingKeyword(SyntaxKind.SyncLockKeyword))
                    errorId = ERRID.ERR_ExpectedEndSyncLock

                Case SyntaxKind.SelectBlock
                    endStmt = SyntaxFactory.EndSelectStatement(missingEndKeyword, InternalSyntaxFactory.MissingKeyword(SyntaxKind.SelectKeyword))
                    errorId = ERRID.ERR_ExpectedEndSelect

                Case SyntaxKind.TryBlock
                    endStmt = SyntaxFactory.EndTryStatement(missingEndKeyword, InternalSyntaxFactory.MissingKeyword(SyntaxKind.TryKeyword))
                    errorId = ERRID.ERR_ExpectedEndTry

                Case SyntaxKind.UsingBlock
                    endStmt = SyntaxFactory.EndUsingStatement(missingEndKeyword, InternalSyntaxFactory.MissingKeyword(SyntaxKind.UsingKeyword))
                    errorId = ERRID.ERR_ExpectedEndUsing

                Case Else
                    Throw ExceptionUtilities.UnexpectedValue(kind)
            End Select

            Return endStmt
        End Function

        Private Shared Function GetEndKind(kind As SyntaxKind) As SyntaxKind
            Select Case kind
                Case SyntaxKind.CompilationUnit,
                    SyntaxKind.SingleLineFunctionLambdaExpression,
                    SyntaxKind.SingleLineSubLambdaExpression
                    Return SyntaxKind.None

                Case SyntaxKind.NamespaceBlock
                    Return SyntaxKind.EndNamespaceStatement

                Case SyntaxKind.ModuleBlock
                    Return SyntaxKind.EndModuleStatement

                Case SyntaxKind.ClassBlock
                    Return SyntaxKind.EndClassStatement

                Case SyntaxKind.StructureBlock
                    Return SyntaxKind.EndStructureStatement

                Case SyntaxKind.InterfaceBlock
                    Return SyntaxKind.EndInterfaceStatement

                Case SyntaxKind.EnumBlock
                    Return SyntaxKind.EndEnumStatement

                Case SyntaxKind.SubBlock,
                    SyntaxKind.ConstructorBlock,
                    SyntaxKind.MultiLineSubLambdaExpression
                    Return SyntaxKind.EndSubStatement

                Case SyntaxKind.FunctionBlock,
                    SyntaxKind.MultiLineFunctionLambdaExpression
                    Return SyntaxKind.EndFunctionStatement

                Case SyntaxKind.OperatorBlock
                    Return SyntaxKind.EndOperatorStatement

                Case SyntaxKind.PropertyBlock
                    Return SyntaxKind.EndPropertyStatement

                Case SyntaxKind.GetAccessorBlock
                    Return SyntaxKind.EndGetStatement

                Case SyntaxKind.SetAccessorBlock
                    Return SyntaxKind.EndSetStatement

                Case SyntaxKind.EventBlock
                    Return SyntaxKind.EndEventStatement

                Case SyntaxKind.AddHandlerAccessorBlock
                    Return SyntaxKind.EndAddHandlerStatement

                Case SyntaxKind.RemoveHandlerAccessorBlock
                    Return SyntaxKind.EndRemoveHandlerStatement

                Case SyntaxKind.RaiseEventAccessorBlock
                    Return SyntaxKind.EndRaiseEventStatement

                Case SyntaxKind.MultiLineIfBlock, SyntaxKind.ElseIfBlock, SyntaxKind.ElseBlock
                    Return SyntaxKind.EndIfStatement

                Case SyntaxKind.SingleLineIfStatement, SyntaxKind.SingleLineElseClause
                    Return SyntaxKind.None

                Case SyntaxKind.SimpleDoLoopBlock,
                     SyntaxKind.DoWhileLoopBlock
                    Return SyntaxKind.SimpleLoopStatement

                Case SyntaxKind.WhileBlock
                    Return SyntaxKind.EndWhileStatement

                Case SyntaxKind.ForBlock, SyntaxKind.ForEachBlock
                    Return SyntaxKind.NextStatement

                Case SyntaxKind.WithBlock
                    Return SyntaxKind.EndWithStatement

                Case SyntaxKind.SyncLockBlock
                    Return SyntaxKind.EndSyncLockStatement

                Case SyntaxKind.SelectBlock, SyntaxKind.CaseBlock, SyntaxKind.CaseElseBlock
                    Return SyntaxKind.EndSelectStatement

                Case SyntaxKind.TryBlock, SyntaxKind.CatchBlock, SyntaxKind.FinallyBlock
                    Return SyntaxKind.EndTryStatement

                Case SyntaxKind.UsingBlock
                    Return SyntaxKind.EndUsingStatement

                Case Else
                    Throw ExceptionUtilities.UnexpectedValue(kind)
            End Select
        End Function

    End Class

End Namespace