|
' 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 Parser
'-----------------------------------------------------------------------------
Imports Microsoft.CodeAnalysis.Syntax.InternalSyntax
Imports InternalSyntaxFactory = Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax.SyntaxFactory
Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax
Partial Friend Class Parser
'
'============ Methods for parsing specific executable statements
'
' [in] Token starting the statement
' File: Parser.cpp
' Lines: 5870 - 5870
' Statement* .Parser::ParseContinueStatement( [ _In_ Token* StmtStart ] [ _Inout_ bool& ErrorInConstruct ] )
Private Function ParseContinueStatement() As ContinueStatementSyntax
Debug.Assert(CurrentToken.Kind = SyntaxKind.ContinueKeyword, "ParseContinueStatement called on wrong token")
Dim continueKeyword As KeywordSyntax = DirectCast(CurrentToken, KeywordSyntax)
GetNextToken()
Dim kind As SyntaxKind
Dim blockKeyword As KeywordSyntax = Nothing
Select Case (CurrentToken.Kind)
Case SyntaxKind.DoKeyword
kind = SyntaxKind.ContinueDoStatement
blockKeyword = DirectCast(CurrentToken, KeywordSyntax)
GetNextToken()
Case SyntaxKind.ForKeyword
kind = SyntaxKind.ContinueForStatement
blockKeyword = DirectCast(CurrentToken, KeywordSyntax)
GetNextToken()
Case SyntaxKind.WhileKeyword
kind = SyntaxKind.ContinueWhileStatement
blockKeyword = DirectCast(CurrentToken, KeywordSyntax)
GetNextToken()
Case Else
' The pretty lister is expected to turn Continue statements
' that don't specify a Do, While or For to have the correct
' form. That requires identifying this condition during
' parsing and correcting the parse trees.
Dim loopContext = Context.FindNearest(AddressOf SyntaxFacts.SupportsContinueStatement)
If loopContext IsNot Nothing Then
Select Case loopContext.BlockKind
Case SyntaxKind.SimpleDoLoopBlock,
SyntaxKind.DoWhileLoopBlock
kind = SyntaxKind.ContinueDoStatement
blockKeyword = InternalSyntaxFactory.MissingKeyword(SyntaxKind.DoKeyword)
Case SyntaxKind.ForBlock, SyntaxKind.ForEachBlock
kind = SyntaxKind.ContinueForStatement
blockKeyword = InternalSyntaxFactory.MissingKeyword(SyntaxKind.ForKeyword)
Case SyntaxKind.WhileBlock
kind = SyntaxKind.ContinueWhileStatement
blockKeyword = InternalSyntaxFactory.MissingKeyword(SyntaxKind.WhileKeyword)
End Select
End If
If blockKeyword Is Nothing Then
' No context found which can have a continue statement was found
' TODO - Which keyword in this case?
kind = SyntaxKind.ContinueDoStatement
blockKeyword = InternalSyntaxFactory.MissingKeyword(SyntaxKind.DoKeyword)
End If
blockKeyword = ReportSyntaxError(blockKeyword, ERRID.ERR_ExpectedContinueKind)
End Select
Dim statement = SyntaxFactory.ContinueStatement(kind, continueKeyword, blockKeyword)
Return statement
End Function
Private Function ParseExitStatement() As StatementSyntax
Debug.Assert(CurrentToken.Kind = SyntaxKind.ExitKeyword, "ParseExitStatement called on wrong token")
Dim exitKeyword As KeywordSyntax = DirectCast(CurrentToken, KeywordSyntax)
GetNextToken()
Dim statement As StatementSyntax = Nothing
Dim kind As SyntaxKind
Dim blockKeyword As KeywordSyntax = Nothing
Select Case (CurrentToken.Kind)
Case SyntaxKind.DoKeyword
kind = SyntaxKind.ExitDoStatement
blockKeyword = DirectCast(CurrentToken, KeywordSyntax)
GetNextToken()
Case SyntaxKind.ForKeyword
kind = SyntaxKind.ExitForStatement
blockKeyword = DirectCast(CurrentToken, KeywordSyntax)
GetNextToken()
Case SyntaxKind.WhileKeyword
kind = SyntaxKind.ExitWhileStatement
blockKeyword = DirectCast(CurrentToken, KeywordSyntax)
GetNextToken()
Case SyntaxKind.SelectKeyword
kind = SyntaxKind.ExitSelectStatement
blockKeyword = DirectCast(CurrentToken, KeywordSyntax)
GetNextToken()
' The pretty lister is expected to turn Exit statements
' that don
' statements that do. That requires identifying this
' condition during parsing and correcting the parse trees.
Case SyntaxKind.SubKeyword
' Error message moved to context
kind = SyntaxKind.ExitSubStatement
blockKeyword = DirectCast(CurrentToken, KeywordSyntax)
GetNextToken()
Case SyntaxKind.FunctionKeyword
' Error message moved to context
kind = SyntaxKind.ExitFunctionStatement
blockKeyword = DirectCast(CurrentToken, KeywordSyntax)
GetNextToken()
Case SyntaxKind.PropertyKeyword
' Error message moved to context
kind = SyntaxKind.ExitPropertyStatement
blockKeyword = DirectCast(CurrentToken, KeywordSyntax)
GetNextToken()
Case SyntaxKind.TryKeyword
kind = SyntaxKind.ExitTryStatement
blockKeyword = DirectCast(CurrentToken, KeywordSyntax)
GetNextToken()
Case Else
'The block keyword is missing. Look at the contexts to determine what should be here
Dim loopContext = Context.FindNearest(AddressOf SyntaxFacts.SupportsExitStatement)
If loopContext IsNot Nothing Then
Select Case loopContext.BlockKind
Case SyntaxKind.SimpleDoLoopBlock,
SyntaxKind.DoWhileLoopBlock
kind = SyntaxKind.ExitDoStatement
blockKeyword = InternalSyntaxFactory.MissingKeyword(SyntaxKind.DoKeyword)
Case SyntaxKind.ForBlock, SyntaxKind.ForEachBlock
kind = SyntaxKind.ExitForStatement
blockKeyword = InternalSyntaxFactory.MissingKeyword(SyntaxKind.ForKeyword)
Case SyntaxKind.WhileBlock
kind = SyntaxKind.ExitWhileStatement
blockKeyword = InternalSyntaxFactory.MissingKeyword(SyntaxKind.WhileKeyword)
Case SyntaxKind.SelectBlock
kind = SyntaxKind.ExitSelectStatement
blockKeyword = InternalSyntaxFactory.MissingKeyword(SyntaxKind.SelectKeyword)
Case SyntaxKind.SubBlock, SyntaxKind.ConstructorBlock
kind = SyntaxKind.ExitSubStatement
blockKeyword = InternalSyntaxFactory.MissingKeyword(SyntaxKind.SubKeyword)
Case SyntaxKind.FunctionBlock
kind = SyntaxKind.ExitFunctionStatement
blockKeyword = InternalSyntaxFactory.MissingKeyword(SyntaxKind.FunctionKeyword)
Case SyntaxKind.PropertyBlock
kind = SyntaxKind.ExitPropertyStatement
blockKeyword = InternalSyntaxFactory.MissingKeyword(SyntaxKind.PropertyKeyword)
Case SyntaxKind.TryBlock
kind = SyntaxKind.ExitTryStatement
blockKeyword = InternalSyntaxFactory.MissingKeyword(SyntaxKind.TryKeyword)
End Select
End If
' "Exit Operator" is not a valid exit kind, but we keep track of when
' a user might be expected to use it to give a smarter error.
' ""Exit Operator' is not valid. Use 'Return' to exit an Operator.'
' Or
' ""Exit AddHandler', 'Exit RemoveHandler' and 'Exit RaiseEvent' are not valid. Use 'Return' to exit from event members.'
Dim stmtError As ERRID = Nothing
Select Case CurrentToken.Kind
Case SyntaxKind.OperatorKeyword
stmtError = ERRID.ERR_ExitOperatorNotValid
Case SyntaxKind.AddHandlerKeyword,
SyntaxKind.RemoveHandlerKeyword,
SyntaxKind.RaiseEventKeyword
stmtError = ERRID.ERR_ExitEventMemberNotInvalid
End Select
If stmtError <> ERRID.ERR_None Then
statement = SyntaxFactory.ReturnStatement(InternalSyntaxFactory.MissingKeyword(SyntaxKind.ReturnKeyword), Nothing)
statement = statement.AddLeadingSyntax(
CodeAnalysis.Syntax.InternalSyntax.SyntaxList.List(exitKeyword, CurrentToken), stmtError)
GetNextToken()
Return statement
End If
If blockKeyword Is Nothing Then
' No context found which can have an exit statement was found
'TODO - What kind of statement should be generated? Bad Exit would make most sense.
' For now generate an Exit Sub but this leads to spurious errors.
kind = SyntaxKind.ExitSubStatement
blockKeyword = InternalSyntaxFactory.MissingKeyword(SyntaxKind.SubKeyword)
End If
blockKeyword = ReportSyntaxError(blockKeyword, ERRID.ERR_ExpectedExitKind)
End Select
statement = SyntaxFactory.ExitStatement(kind, exitKeyword, blockKeyword)
Return statement
End Function
Private Function ParseCaseStatement() As CaseStatementSyntax
Debug.Assert(CurrentToken.Kind = SyntaxKind.CaseKeyword, "ParseCaseStatement called on wrong token.")
Dim caseKeyword As KeywordSyntax = DirectCast(CurrentToken, KeywordSyntax)
GetNextToken()
Dim caseClauses = _pool.AllocateSeparated(Of CaseClauseSyntax)()
Dim elseKeyword As KeywordSyntax = Nothing
If CurrentToken.Kind = SyntaxKind.ElseKeyword Then
elseKeyword = DirectCast(CurrentToken, KeywordSyntax)
GetNextToken() '// get off ELSE
Dim caseClause = SyntaxFactory.ElseCaseClause(elseKeyword)
caseClauses.Add(caseClause)
Else
Do
Dim StartCase As SyntaxKind = CurrentToken.Kind ' dev10_500588 Snap the start of the expression token AFTER we've moved off the EOL (if one is present)
Dim caseClause As CaseClauseSyntax
If StartCase = SyntaxKind.IsKeyword OrElse SyntaxFacts.IsRelationalOperator(StartCase) Then
' dev10_526560 Allow implicit newline after IS
Dim optionalIsKeyword As KeywordSyntax = Nothing
TryGetTokenAndEatNewLine(SyntaxKind.IsKeyword, optionalIsKeyword)
If SyntaxFacts.IsRelationalOperator(CurrentToken.Kind) Then
Dim relationalOperator = DirectCast(CurrentToken, PunctuationSyntax)
GetNextToken() ' get off relational operator
TryEatNewLine() ' dev10_503248
Dim CaseExpr As ExpressionSyntax = ParseExpressionCore()
If CaseExpr.ContainsDiagnostics Then
CaseExpr = ResyncAt(CaseExpr)
End If
caseClause = SyntaxFactory.RelationalCaseClause(RelationalOperatorKindToCaseKind(relationalOperator.Kind), optionalIsKeyword, relationalOperator, CaseExpr)
Else
' Since we saw IS, create a relational case.
' This helps intellisense do a drop down of
' the operators that can follow "Is".
Dim relationalOperator = ReportSyntaxError(InternalSyntaxFactory.MissingPunctuation(SyntaxKind.EqualsToken), ERRID.ERR_ExpectedRelational)
caseClause = ResyncAt(InternalSyntaxFactory.RelationalCaseClause(SyntaxKind.CaseEqualsClause, optionalIsKeyword, relationalOperator, InternalSyntaxFactory.MissingExpression))
End If
Else
Dim value As ExpressionSyntax = ParseExpressionCore()
If value.ContainsDiagnostics Then
value = ResyncAt(value, SyntaxKind.ToKeyword)
End If
Dim toKeyword As KeywordSyntax = Nothing
If TryGetToken(SyntaxKind.ToKeyword, toKeyword) Then
Dim upperBound As ExpressionSyntax = ParseExpressionCore()
If upperBound.ContainsDiagnostics Then
upperBound = ResyncAt(upperBound)
End If
caseClause = SyntaxFactory.RangeCaseClause(value, toKeyword, upperBound)
Else
caseClause = SyntaxFactory.SimpleCaseClause(value)
End If
End If
caseClauses.Add(caseClause)
Dim comma As PunctuationSyntax = Nothing
If Not TryGetTokenAndEatNewLine(SyntaxKind.CommaToken, comma) Then
Exit Do
End If
caseClauses.AddSeparator(comma)
Loop
End If
Dim separatedCaseClauses = caseClauses.ToList()
_pool.Free(caseClauses)
Dim statement As CaseStatementSyntax
If elseKeyword Is Nothing Then
statement = SyntaxFactory.CaseStatement(caseKeyword, separatedCaseClauses)
Else
statement = SyntaxFactory.CaseElseStatement(caseKeyword, separatedCaseClauses)
End If
Return statement
End Function
Private Shared Function RelationalOperatorKindToCaseKind(kind As SyntaxKind) As SyntaxKind
Select Case kind
Case SyntaxKind.LessThanToken
Return SyntaxKind.CaseLessThanClause
Case SyntaxKind.LessThanEqualsToken
Return SyntaxKind.CaseLessThanOrEqualClause
Case SyntaxKind.EqualsToken
Return SyntaxKind.CaseEqualsClause
Case SyntaxKind.LessThanGreaterThanToken
Return SyntaxKind.CaseNotEqualsClause
Case SyntaxKind.GreaterThanToken
Return SyntaxKind.CaseGreaterThanClause
Case SyntaxKind.GreaterThanEqualsToken
Return SyntaxKind.CaseGreaterThanOrEqualClause
Case Else
Debug.Assert(False, "Wrong relational operator kind")
Return Nothing
End Select
End Function
Private Function ParseSelectStatement() As SelectStatementSyntax
Debug.Assert(CurrentToken.Kind = SyntaxKind.SelectKeyword, "ParseSelectStatement called on wrong token.")
Dim selectKeyword As KeywordSyntax = DirectCast(CurrentToken, KeywordSyntax)
GetNextToken() ' get off SELECT
' Allow the expected CASE token to be present or not.
Dim optionalCaseKeyword As KeywordSyntax = Nothing
TryGetToken(SyntaxKind.CaseKeyword, optionalCaseKeyword)
Dim value As ExpressionSyntax = ParseExpressionCore()
If value.ContainsDiagnostics Then
value = ResyncAt(value)
End If
Dim statement = SyntaxFactory.SelectStatement(selectKeyword, optionalCaseKeyword, value)
Return statement
End Function
' ParseIfConstruct handles the parsing of block and line if statements and
' block and line elseif statements, setting *IsLineIf as appropriate.
' For a line if/elseif, parsing consumes through the first statement (if any)
' in the then clause. The returned tree is the block created for the if or else if.
' Parse the expression (and following text) in an If or ElseIf statement.
' File: Parser.cpp
' Lines: 11249 - 11249
' ExpressionBlockStatement* .Parser::ParseIfConstruct( [ ParseTree::IfStatement* IfContainingElseIf ] [ _Out_ bool* IsLineIf ] [ _Inout_ bool& ErrorInConstruct ] )
'davidsch - Renamed ParseIfStatement from ParseIfConstruct
Private Function ParseIfStatement() As IfStatementSyntax
Debug.Assert(CurrentToken.Kind = SyntaxKind.IfKeyword, "ParseIfConstruct called on wrong token.")
Dim ifKeyword As KeywordSyntax = DirectCast(CurrentToken, KeywordSyntax)
GetNextToken()
Dim condition = ParseExpressionCore(OperatorPrecedence.PrecedenceNone)
If condition.ContainsDiagnostics Then
condition = ResyncAt(condition, SyntaxKind.ThenKeyword)
End If
Dim thenKeyword As KeywordSyntax = Nothing
TryGetToken(SyntaxKind.ThenKeyword, thenKeyword)
Dim statement = SyntaxFactory.IfStatement(ifKeyword, condition, thenKeyword)
Return statement
End Function
Private Function ParseElseStatement() As ElseStatementSyntax
Debug.Assert(CurrentToken.Kind = SyntaxKind.ElseKeyword, "ParseIfConstruct called on wrong token.")
Dim elseKeyword = DirectCast(CurrentToken, KeywordSyntax)
GetNextToken()
Dim statement = SyntaxFactory.ElseStatement(elseKeyword)
Return statement
End Function
Private Function ParseElseIfStatement() As StatementSyntax
Debug.Assert(CurrentToken.Kind = SyntaxKind.ElseIfKeyword OrElse (CurrentToken.Kind = SyntaxKind.ElseKeyword AndAlso PeekToken(1).Kind = SyntaxKind.IfKeyword),
"ParseIfConstruct called on wrong token.")
Dim elseIfKeyword As KeywordSyntax = Nothing
If CurrentToken.Kind = SyntaxKind.ElseIfKeyword Then
elseIfKeyword = DirectCast(CurrentToken, KeywordSyntax)
GetNextToken()
ElseIf CurrentToken.Kind = SyntaxKind.ElseKeyword Then
' When written as 'Else If' we need to merged the two keywords together.
Dim elseKeyword = DirectCast(CurrentToken, KeywordSyntax)
GetNextToken()
If Context.IsSingleLine Then
' But inside of a single-line If this isn't allowed. We parse as an Else statement
' so that the SingleLineIfBlockContext can parse the rest as a separate If statement.
Return SyntaxFactory.ElseStatement(elseKeyword)
End If
Dim ifKeyword = DirectCast(CurrentToken, KeywordSyntax)
GetNextToken()
elseIfKeyword = New KeywordSyntax(SyntaxKind.ElseIfKeyword, MergeTokenText(elseKeyword, ifKeyword), elseKeyword.GetLeadingTrivia(), ifKeyword.GetTrailingTrivia())
End If
Dim condition = ParseExpressionCore(OperatorPrecedence.PrecedenceNone)
If condition.ContainsDiagnostics Then
condition = ResyncAt(condition, SyntaxKind.ThenKeyword)
End If
Dim thenKeyword As KeywordSyntax = Nothing
TryGetToken(SyntaxKind.ThenKeyword, thenKeyword)
Dim statement = SyntaxFactory.ElseIfStatement(elseIfKeyword, condition, thenKeyword)
Return statement
End Function
Private Function ParseAnachronisticStatement() As StatementSyntax
' Assume CurrentToken is on ENDIF, WEND
Debug.Assert(CurrentToken.Kind = SyntaxKind.EndIfKeyword OrElse
CurrentToken.Kind = SyntaxKind.GosubKeyword OrElse
CurrentToken.Kind = SyntaxKind.WendKeyword, "ParseAnachronisticEndIfStatement called on wrong token")
Dim keyword As KeywordSyntax = DirectCast(CurrentToken, KeywordSyntax)
' Put the 'ENDIF'/'WEND'/'GOSUB' token in the unexpected.
Dim unexpected As CodeAnalysis.Syntax.InternalSyntax.SyntaxList(Of SyntaxToken) = ResyncAt()
Dim missingEndKeyword As KeywordSyntax = InternalSyntaxFactory.MissingKeyword(SyntaxKind.EndKeyword)
Dim statement As StatementSyntax = Nothing
Dim errorId As ERRID
Select Case keyword.Kind
Case SyntaxKind.EndIfKeyword
' EndIf is anachronistic - Correct syntax is END IF
' TODO - Is an END IF with missing tokens the right way to model this?
statement = SyntaxFactory.EndIfStatement(missingEndKeyword, InternalSyntaxFactory.MissingKeyword(SyntaxKind.IfKeyword))
errorId = ERRID.ERR_ObsoleteEndIf
Case SyntaxKind.WendKeyword
' While...Wend are anachronistic
' TODO - Is an END IF with missing tokens the right way to model this?
statement = SyntaxFactory.EndWhileStatement(missingEndKeyword, InternalSyntaxFactory.MissingKeyword(SyntaxKind.WhileKeyword))
errorId = ERRID.ERR_ObsoleteWhileWend
Case SyntaxKind.GosubKeyword
statement = InternalSyntaxFactory.EmptyStatement
errorId = ERRID.ERR_ObsoleteGosub
End Select
'The Dev10 parser does not mark this statement as bad.
Return statement.AddTrailingSyntax(unexpected, errorId)
End Function
Private Function ParseDoStatement() As DoStatementSyntax
' Assume CurrentToken is on Do
Debug.Assert(CurrentToken.Kind = SyntaxKind.DoKeyword, "ParseDoStatement called on wrong token")
Dim doKeyword = DirectCast(CurrentToken, KeywordSyntax)
' Consume the Do.
GetNextToken()
Dim optionalWhileOrUntilClause As WhileOrUntilClauseSyntax = Nothing
TryParseOptionalWhileOrUntilClause(doKeyword, optionalWhileOrUntilClause)
Dim kind As SyntaxKind
If optionalWhileOrUntilClause Is Nothing Then
kind = SyntaxKind.SimpleDoStatement
ElseIf optionalWhileOrUntilClause.Kind = SyntaxKind.WhileClause Then
kind = SyntaxKind.DoWhileStatement
Else
kind = SyntaxKind.DoUntilStatement
End If
Dim statement As DoStatementSyntax = SyntaxFactory.DoStatement(kind, doKeyword, optionalWhileOrUntilClause)
Return statement
End Function
Private Function ParseLoopStatement() As LoopStatementSyntax
' Assume CurrentToken is on Loop
Debug.Assert(CurrentToken.Kind = SyntaxKind.LoopKeyword, "ParseDoStatement called on wrong token")
Dim loopKeyword = DirectCast(CurrentToken, KeywordSyntax)
GetNextToken()
' Moved ERRID.ERR_LoopDoubleCondition to TryParseOptionalWhileOrUntilClause
Dim optionalWhileOrUntilClause As WhileOrUntilClauseSyntax = Nothing
TryParseOptionalWhileOrUntilClause(loopKeyword, optionalWhileOrUntilClause)
Dim kind As SyntaxKind
If optionalWhileOrUntilClause Is Nothing Then
kind = SyntaxKind.SimpleLoopStatement
ElseIf optionalWhileOrUntilClause.Kind = SyntaxKind.WhileClause Then
kind = SyntaxKind.LoopWhileStatement
Else
kind = SyntaxKind.LoopUntilStatement
End If
Dim statement As LoopStatementSyntax = SyntaxFactory.LoopStatement(kind, loopKeyword, optionalWhileOrUntilClause)
Return statement
End Function
Private Function ParseForStatement() As StatementSyntax
' Assume CurrentToken is on For
Debug.Assert(CurrentToken.Kind = SyntaxKind.ForKeyword, "ParseForStatement called on wrong token")
Dim forKeyword As KeywordSyntax = DirectCast(CurrentToken, KeywordSyntax)
' Consume the FOR.
GetNextToken()
Dim eachKeyword As KeywordSyntax = Nothing
If TryGetToken(SyntaxKind.EachKeyword, eachKeyword) Then
Return ParseForEachStatement(forKeyword, eachKeyword)
Else
Return ParseForStatement(forKeyword)
End If
End Function
Private Function ParseForEachStatement(forKeyword As KeywordSyntax, eachKeyword As KeywordSyntax) As ForEachStatementSyntax
Dim controlVariable = ParseForLoopControlVariable()
Dim expression As ExpressionSyntax = Nothing
If controlVariable.ContainsDiagnostics Then
controlVariable = ResyncAt(controlVariable, SyntaxKind.InKeyword)
End If
TryEatNewLineIfFollowedBy(SyntaxKind.InKeyword)
Dim inKeyword As KeywordSyntax = Nothing
If TryGetTokenAndEatNewLine(SyntaxKind.InKeyword, inKeyword) Then
expression = ParseExpressionCore()
If expression.ContainsDiagnostics Then
expression = ResyncAt(expression)
End If
Else
'TODO - In Dev 10 only syntax error was reported. Now
' expected 'In" is reported. Should only 1 message be reported?
' Also see parsing For with expected =.
inKeyword = DirectCast(HandleUnexpectedToken(SyntaxKind.InKeyword), KeywordSyntax)
expression = InternalSyntaxFactory.MissingExpression.AddTrailingSyntax(ResyncAt({SyntaxKind.ToKeyword}), ERRID.ERR_Syntax)
End If
Dim statement = SyntaxFactory.ForEachStatement(forKeyword, eachKeyword, controlVariable, inKeyword, expression)
Return statement
End Function
Private Function ParseForStatement(forKeyword As KeywordSyntax) As ForStatementSyntax
Dim controlVariable = ParseForLoopControlVariable()
Dim fromValue As ExpressionSyntax = Nothing
Dim toValue As ExpressionSyntax = Nothing
If controlVariable.ContainsDiagnostics Then
controlVariable = ResyncAt(controlVariable, SyntaxKind.EqualsToken, SyntaxKind.ToKeyword)
End If
' TODO - Handle case where controlVariable is OK but current token is not equals. Need to resync in that case too
' e.g.
' For i,j = 1 to 10
' See bug 8590.
Dim equalsToken As PunctuationSyntax = Nothing
If TryGetTokenAndEatNewLine(SyntaxKind.EqualsToken, equalsToken) Then
' Dev10_545918 - Allow implicit line continuation after '='
fromValue = ParseExpressionCore()
If fromValue.ContainsDiagnostics Then
fromValue = ResyncAt(fromValue, SyntaxKind.ToKeyword)
End If
Else
'Dev 10 only reported syntax error. Code now reports expected '=' and syntax error.
' TODO - consider removing redundant syntax error message.
equalsToken = DirectCast(HandleUnexpectedToken(SyntaxKind.EqualsToken), PunctuationSyntax)
fromValue = InternalSyntaxFactory.MissingExpression.AddTrailingSyntax(ResyncAt({SyntaxKind.ToKeyword}), ERRID.ERR_Syntax)
End If
Dim toKeyword As KeywordSyntax = Nothing
If TryGetToken(SyntaxKind.ToKeyword, toKeyword) Then
'TODO - davidsch - Why is newline allowed after '=' but not after 'to'?
toValue = ParseExpressionCore()
If toValue.ContainsDiagnostics Then
toValue = ResyncAt(toValue, SyntaxKind.StepKeyword)
End If
Else
'No error for expected 'To' keyword. HandleUnexpectedToken returns ERRID_Syntax
toKeyword = DirectCast(HandleUnexpectedToken(SyntaxKind.ToKeyword), KeywordSyntax)
'TODO - ERRID_ExpectedExpression here?
toValue = InternalSyntaxFactory.MissingExpression.AddTrailingSyntax(ResyncAt({SyntaxKind.ToKeyword}))
End If
Dim optionalStepClause As ForStepClauseSyntax = Nothing
Dim stepKeyword As KeywordSyntax = Nothing
Dim stepValue As ExpressionSyntax = Nothing
If TryGetToken(SyntaxKind.StepKeyword, stepKeyword) Then
stepValue = ParseExpressionCore()
If stepValue.ContainsDiagnostics Then
stepValue = ResyncAt(stepValue)
End If
optionalStepClause = SyntaxFactory.ForStepClause(stepKeyword, stepValue)
End If
Dim statement = SyntaxFactory.ForStatement(forKeyword, controlVariable, equalsToken, fromValue, toKeyword, toValue, optionalStepClause)
Return statement
End Function
Private Function ParseNextStatement() As NextStatementSyntax
' Assume CurrentToken is on Next
Debug.Assert(CurrentToken.Kind = SyntaxKind.NextKeyword, "ParseNextStatement called on wrong token")
Dim nextKeyword As KeywordSyntax = DirectCast(CurrentToken, KeywordSyntax)
GetNextToken()
If CanFollowStatement(CurrentToken) Then
Return SyntaxFactory.NextStatement(nextKeyword, Nothing)
Else
' Collect the next variables.
' Context is updated when statement is processed by ForBlockContext.
Dim variables = _pool.AllocateSeparated(Of ExpressionSyntax)()
Dim enclosing As BlockContext = Context
Do
Dim variable As ExpressionSyntax = ParseVariable()
If variable.ContainsDiagnostics Then
variable = ResyncAt(variable)
End If
' Report ERR_ExtraNextVariable for the first additional Next variable
' only. This matches the behavior of the native compiler and avoids
' walking out too many contexts.
If enclosing IsNot Nothing AndAlso
enclosing.BlockKind <> SyntaxKind.ForBlock AndAlso
enclosing.BlockKind <> SyntaxKind.ForEachBlock Then
variable = ReportSyntaxError(variable, ERRID.ERR_ExtraNextVariable)
' Skip further checks for extra variables.
enclosing = Nothing
End If
variables.Add(variable)
Dim comma As PunctuationSyntax = Nothing
If Not TryGetTokenAndEatNewLine(SyntaxKind.CommaToken, comma) Then
Exit Do
End If
variables.AddSeparator(comma)
If enclosing IsNot Nothing Then
enclosing = enclosing.PrevBlock
Debug.Assert(enclosing IsNot Nothing)
End If
Loop
Dim statement = SyntaxFactory.NextStatement(nextKeyword, variables.ToList)
_pool.Free(variables)
Return statement
End If
End Function
' /*********************************************************************
' *
' * Function:
' * Parser::ParseForLoopControlVariable
' *
' * Purpose:
' * Parses: <Expression> | <ident>[ArrayList] As <type>
' *
' **********************************************************************/
' File: Parser.cpp
' Lines: 5717 - 5717
' Expression* .Parser::ParseForLoopControlVariable( [ ParseTree::BlockStatement* ForBlock ] [ _In_ Token* ForStart ] [ _Out_ ParseTree::VariableDeclarationStatement** Decl ] [ _Inout_ bool& ErrorInConstruct ] )
Private Function ParseForLoopControlVariable() As VisualBasicSyntaxNode
'TODO - davidsch - I have kept this code as-is but it seems that it would be better to parse the common prefix of
' ParseVariable and ParseForLoopVariableDeclaration instead of peeking for 'AS', 'IN', '='
Select Case (CurrentToken.Kind)
Case SyntaxKind.IdentifierToken
Select Case PeekToken(1).Kind
Case SyntaxKind.QuestionToken, SyntaxKind.AsKeyword
Return ParseForLoopVariableDeclaration()
Case SyntaxKind.OpenParenToken
Dim lookAhead As SyntaxToken = Nothing
Dim i = PeekAheadFor(s_isTokenOrKeywordFunc, {SyntaxKind.AsKeyword, SyntaxKind.InKeyword, SyntaxKind.EqualsToken}, lookAhead)
If lookAhead IsNot Nothing AndAlso
lookAhead.Kind = SyntaxKind.AsKeyword AndAlso
PeekToken(i - 1).Kind = SyntaxKind.CloseParenToken Then
Return ParseForLoopVariableDeclaration()
End If
' Fall through to Non-Declaration, i.e. ParseVariable below
End Select
Return ParseVariable()
Case Else
Return ParseVariable()
End Select
End Function
' /*********************************************************************
' *
' * Function:
' * Parser::ParseForLoopVariableDeclaration
' *
' * Purpose:
' * Parses: <ident>[ArrayList] As <type>
' *
' **********************************************************************/
' File: Parser.cpp
' Lines: 5761 - 5761
' Expression* .Parser::ParseForLoopVariableDeclaration( [ ParseTree::BlockStatement* ForBlock ] [ _In_ Token* ForStart ] [ _Out_ ParseTree::VariableDeclarationStatement** Decl ] [ _Inout_ bool& ErrorInConstruct ] )
Private Function ParseForLoopVariableDeclaration() As VariableDeclaratorSyntax
' Parse the control variable declaration
Dim Declarator = ParseModifiedIdentifier(True, False)
If Declarator.ContainsDiagnostics Then
' If we see As before a In or Each, then assume that
' we are still on the Control Variable Declaration.
' Otherwise, don't resync and allow the caller to
' decide how to recover.
If PeekAheadFor(SyntaxKind.AsKeyword, SyntaxKind.InKeyword, SyntaxKind.EqualsToken) = SyntaxKind.AsKeyword Then
Declarator = ResyncAt(Declarator, SyntaxKind.AsKeyword)
End If
End If
Dim [As] As KeywordSyntax = Nothing
Dim Type As TypeSyntax = Nothing
Dim optionalAsClause As AsClauseSyntax = Nothing
If CurrentToken.Kind = SyntaxKind.AsKeyword Then
[As] = DirectCast(CurrentToken, KeywordSyntax)
' Parse the type
GetNextToken()
Type = ParseGeneralType()
optionalAsClause = SyntaxFactory.SimpleAsClause([As], Nothing, Type)
End If ' Else if "As" is not present, the error falls out as a "Syntax error" IN the caller
Dim names = _pool.AllocateSeparated(Of ModifiedIdentifierSyntax)()
names.Add(Declarator)
Dim result = SyntaxFactory.VariableDeclarator(names.ToList, optionalAsClause, Nothing)
_pool.Free(names)
Return result
End Function
' Parse a reference to a label, which can be an identifier or a line number.
' File: Parser.cpp
' Lines: 11363 - 11363
' .Parser::ParseLabelReference( [ _Out_ ParseTree::LabelReferenceStatement* LabelReference ] [ _Inout_ bool& ErrorInConstruct ] )
Private Function ParseLabelReference() As SyntaxToken
Dim label As SyntaxToken = CurrentToken
If label.Kind = SyntaxKind.IdentifierToken Then
Dim ident As IdentifierTokenSyntax = DirectCast(label, IdentifierTokenSyntax)
If ident.TypeCharacter <> TypeCharacter.None Then
label = ReportSyntaxError(label, ERRID.ERR_NoTypecharInLabel)
GetNextToken()
Return label
End If
Return ParseIdentifier()
ElseIf label.Kind = SyntaxKind.IntegerLiteralToken Then
Dim intLiteral As IntegerLiteralTokenSyntax = DirectCast(label, IntegerLiteralTokenSyntax)
If Not intLiteral.ContainsDiagnostics Then
If intLiteral.TypeSuffix = TypeCharacter.None Then
' Labels must be unsigned. Reinterpret label as a ulong / uint in case it was a negative number written in hex &hffffffff or octal.
Dim intLiteralValue As ULong = CULng(intLiteral.ObjectValue)
If TypeOf intLiteral Is IntegerLiteralTokenSyntax(Of Integer) Then
intLiteralValue = CUInt(intLiteralValue)
End If
intLiteral = New IntegerLiteralTokenSyntax(Of ULong)(SyntaxKind.IntegerLiteralToken, intLiteral.ToString, intLiteral.GetLeadingTrivia, intLiteral.GetTrailingTrivia, intLiteral.Base, TypeCharacter.None, intLiteralValue)
Else
intLiteral = ReportSyntaxError(intLiteral, ERRID.ERR_Syntax)
End If
End If
GetNextToken()
Return intLiteral
Else
label = InternalSyntaxFactory.MissingIdentifier()
label = ReportSyntaxError(label, ERRID.ERR_ExpectedIdentifier)
Return label
End If
End Function
' File: Parser.cpp
' Lines: 11410 - 11410
' .Parser::ParseGotoStatement( [ _Inout_ bool& ErrorInConstruct ] )
Private Function ParseGotoStatement() As StatementSyntax
Debug.Assert(CurrentToken.Kind = SyntaxKind.GoToKeyword, "Alleged GOTO isn't.")
Dim gotoKeyword As KeywordSyntax = DirectCast(CurrentToken, KeywordSyntax)
GetNextToken()
Dim labelName = ParseLabelReference()
'TODO - Not calling ResyncAt may cause different errors from Dev10.
'Dev10 calls ResyncAt here if the label has an error. However, this is
' unnecessary now because GetStatementTerminator will do the resync.
Dim gotoStmt = SyntaxFactory.GoToStatement(gotoKeyword, GetLabelSyntaxForIdentifierOrLineNumber(labelName))
Return gotoStmt
End Function
Private Function GetLabelSyntaxForIdentifierOrLineNumber(ByVal labelName As SyntaxToken) As LabelSyntax
Debug.Assert(labelName.Kind = SyntaxKind.IntegerLiteralToken OrElse labelName.Kind = SyntaxKind.IdentifierToken)
Return If(labelName.Kind = SyntaxKind.IntegerLiteralToken, SyntaxFactory.NumericLabel(labelName), SyntaxFactory.IdentifierLabel(labelName))
End Function
' File: Parser.cpp
' Lines: 11440 - 11440
' .Parser::ParseOnErrorStatement( [ _Inout_ bool& ErrorInConstruct ] )
Private Function ParseOnErrorStatement() As StatementSyntax
Debug.Assert(CurrentToken.Kind = SyntaxKind.OnKeyword, "ON statement must start with ON.")
Dim onKeyword As KeywordSyntax = DirectCast(CurrentToken, KeywordSyntax)
GetNextToken()
Dim errorKeyword As KeywordSyntax
If CurrentToken.Kind = SyntaxKind.ErrorKeyword Then
errorKeyword = DirectCast(CurrentToken, KeywordSyntax)
GetNextToken()
Else
errorKeyword = ReportSyntaxError(InternalSyntaxFactory.MissingKeyword(SyntaxKind.ErrorKeyword), ERRID.ERR_ObsoleteOnGotoGosub)
errorKeyword = ResyncAt(errorKeyword, SyntaxKind.GoToKeyword, SyntaxKind.ResumeKeyword)
End If
If CurrentToken.Kind = SyntaxKind.ResumeKeyword Then
Return ParseOnErrorResumeNext(onKeyword, errorKeyword)
ElseIf CurrentToken.Kind = SyntaxKind.GoToKeyword Then
Return ParseOnErrorGoto(onKeyword, errorKeyword)
Else
Dim missingGotoKeyword = InternalSyntaxFactory.MissingKeyword(SyntaxKind.GoToKeyword)
If Not errorKeyword.ContainsDiagnostics Then
missingGotoKeyword = ReportSyntaxError(missingGotoKeyword, ERRID.ERR_ExpectedResumeOrGoto)
End If
Dim statement = SyntaxFactory.OnErrorGoToStatement(SyntaxKind.OnErrorGoToLabelStatement,
onKeyword,
errorKeyword,
missingGotoKeyword,
Nothing,
SyntaxFactory.IdentifierLabel(InternalSyntaxFactory.MissingIdentifier()))
Return statement
End If
End Function
Private Function ParseOnErrorResumeNext(onKeyword As KeywordSyntax, errorKeyword As KeywordSyntax) As OnErrorResumeNextStatementSyntax
Debug.Assert(CurrentToken.Kind = SyntaxKind.ResumeKeyword, "ParseOnErrorResumeNext called on wrong token.")
Dim resumeKeyword As KeywordSyntax = DirectCast(CurrentToken, KeywordSyntax)
Dim nextKeyword As KeywordSyntax = Nothing
GetNextToken()
VerifyExpectedToken(SyntaxKind.NextKeyword, nextKeyword)
Dim statement = SyntaxFactory.OnErrorResumeNextStatement(onKeyword, errorKeyword, resumeKeyword, nextKeyword)
Return statement
End Function
Private Function ParseOnErrorGoto(onKeyword As KeywordSyntax, errorKeyword As KeywordSyntax) As OnErrorGoToStatementSyntax
Debug.Assert(CurrentToken.Kind = SyntaxKind.GoToKeyword, "ParseOnErrorGoto called on wrong token.")
Dim gotoKeyword = DirectCast(CurrentToken, KeywordSyntax)
Dim optionalMinusToken As PunctuationSyntax = Nothing
Dim label As LabelSyntax
Dim kind As SyntaxKind
Dim nextToken As SyntaxToken = PeekToken(1)
If nextToken.Kind = SyntaxKind.IntegerLiteralToken AndAlso
nextToken.ValueText = "0" Then
kind = SyntaxKind.OnErrorGoToZeroStatement
label = SyntaxFactory.NumericLabel(nextToken)
GetNextToken()
GetNextToken()
ElseIf nextToken.Kind = SyntaxKind.MinusToken AndAlso
PeekToken(2).Kind = SyntaxKind.IntegerLiteralToken AndAlso
PeekToken(2).ValueText = "1" Then
kind = SyntaxKind.OnErrorGoToMinusOneStatement
optionalMinusToken = DirectCast(nextToken, PunctuationSyntax)
label = SyntaxFactory.NumericLabel(PeekToken(2))
GetNextToken()
GetNextToken()
GetNextToken()
Else
GetNextToken()
kind = SyntaxKind.OnErrorGoToLabelStatement
label = GetLabelSyntaxForIdentifierOrLineNumber(ParseLabelReference())
End If
Dim statement = SyntaxFactory.OnErrorGoToStatement(kind, onKeyword, errorKeyword, gotoKeyword, optionalMinusToken, label)
Return statement
End Function
' File: Parser.cpp
' Lines: 11551 - 11551
' .Parser::ParseResumeStatement( [ _Inout_ bool& ErrorInConstruct ] )
Private Function ParseResumeStatement() As ResumeStatementSyntax
Debug.Assert(CurrentToken.Kind = SyntaxKind.ResumeKeyword, "ParseResumeStatement called on wrong token.")
Dim resumeKeyword As KeywordSyntax = DirectCast(CurrentToken, KeywordSyntax)
Dim optionalLabel As SyntaxToken = Nothing
Dim statement As ResumeStatementSyntax
GetNextToken()
If Not IsValidStatementTerminator(CurrentToken) Then
If CurrentToken.Kind = SyntaxKind.NextKeyword Then
Dim nextKeyword = DirectCast(CurrentToken, KeywordSyntax)
GetNextToken()
statement = SyntaxFactory.ResumeNextStatement(resumeKeyword, SyntaxFactory.NextLabel(nextKeyword))
Else
optionalLabel = ParseLabelReference()
statement = SyntaxFactory.ResumeLabelStatement(resumeKeyword, GetLabelSyntaxForIdentifierOrLineNumber(optionalLabel))
End If
Else
statement = SyntaxFactory.ResumeStatement(resumeKeyword, Nothing)
End If
Return statement
End Function
Private Function ParseAssignmentOrInvocationStatement() As StatementSyntax
Dim target As ExpressionSyntax = ParseTerm()
If target.ContainsDiagnostics Then
target = ResyncAt(target, SyntaxKind.EqualsToken)
End If
' Could be a function call or it could be an assignment
If SyntaxFacts.IsAssignmentStatementOperatorToken(CurrentToken.Kind) Then
' Consume the assignment operator
Dim operatorToken As PunctuationSyntax = DirectCast(CurrentToken, PunctuationSyntax)
GetNextToken()
TryEatNewLine()
Dim source = ParseExpressionCore()
If source.ContainsDiagnostics Then
' Sync to avoid other errors
source = ResyncAt(source)
End If
Return MakeAssignmentStatement(target, operatorToken, source)
End If
Return SyntaxFactory.ExpressionStatement(MakeInvocationExpression(target))
End Function
Private Function MakeInvocationExpression(target As ExpressionSyntax) As ExpressionSyntax
' Dig into conditional access
If target.Kind = SyntaxKind.ConditionalAccessExpression Then
Dim conditionalTarget = DirectCast(target, ConditionalAccessExpressionSyntax)
Dim invocation = MakeInvocationExpression(conditionalTarget.WhenNotNull)
If conditionalTarget.WhenNotNull IsNot invocation Then
target = SyntaxFactory.ConditionalAccessExpression(conditionalTarget.Expression, conditionalTarget.QuestionMarkToken, invocation)
End If
ElseIf target.Kind <> SyntaxKind.InvocationExpression Then ' VS320205
If Not CanEndExecutableStatement(CurrentToken) AndAlso
CurrentToken.Kind <> SyntaxKind.BadToken AndAlso
target.Kind <> SyntaxKind.PredefinedCastExpression Then
'TODO - Are there other built-in types that should not through this path?, i.e
'NodeKind.GetTypeKeyword,
'NodeKind.GetXmlNamespaceKeyword
' See call to ParseAssignmentOrCallStatement
' Actually why are built in casts treated differently??
' absence of parentheses and act as if they were present.
' A non-parenthesized argument list cannot contain
' a newline.
Dim unexpected As GreenNode = Nothing
Dim arguments = ParseArguments(unexpected)
Dim closeParen = InternalSyntaxFactory.MissingPunctuation(SyntaxKind.CloseParenToken)
If unexpected IsNot Nothing Then
closeParen = closeParen.AddLeadingSyntax(unexpected)
End If
Dim argumentList = SyntaxFactory.ArgumentList(InternalSyntaxFactory.MissingPunctuation(SyntaxKind.OpenParenToken),
arguments,
closeParen)
target = SyntaxFactory.InvocationExpression(target, ReportSyntaxError(argumentList, ERRID.ERR_ObsoleteArgumentsNeedParens))
Else
target = SyntaxFactory.InvocationExpression(target, Nothing)
End If
End If
Return target
End Function
Private Function MakeAssignmentStatement(left As ExpressionSyntax, operatorToken As PunctuationSyntax, right As ExpressionSyntax) As AssignmentStatementSyntax
Select Case operatorToken.Kind
Case SyntaxKind.EqualsToken
Return SyntaxFactory.SimpleAssignmentStatement(left, operatorToken, right)
Case SyntaxKind.PlusEqualsToken
Return SyntaxFactory.AddAssignmentStatement(left, operatorToken, right)
Case SyntaxKind.MinusEqualsToken
Return SyntaxFactory.SubtractAssignmentStatement(left, operatorToken, right)
Case SyntaxKind.AsteriskEqualsToken
Return SyntaxFactory.MultiplyAssignmentStatement(left, operatorToken, right)
Case SyntaxKind.SlashEqualsToken
Return SyntaxFactory.DivideAssignmentStatement(left, operatorToken, right)
Case SyntaxKind.BackslashEqualsToken
Return SyntaxFactory.IntegerDivideAssignmentStatement(left, operatorToken, right)
Case SyntaxKind.CaretEqualsToken
Return SyntaxFactory.ExponentiateAssignmentStatement(left, operatorToken, right)
Case SyntaxKind.LessThanLessThanEqualsToken
Return SyntaxFactory.LeftShiftAssignmentStatement(left, operatorToken, right)
Case SyntaxKind.GreaterThanGreaterThanEqualsToken
Return SyntaxFactory.RightShiftAssignmentStatement(left, operatorToken, right)
Case SyntaxKind.AmpersandEqualsToken
Return SyntaxFactory.ConcatenateAssignmentStatement(left, operatorToken, right)
Case Else
Throw ExceptionUtilities.UnexpectedValue(operatorToken.Kind)
End Select
End Function
' File: Parser.cpp
' Lines: 11600 - 11600
' .Parser::ParseCallStatement( [ _Inout_ bool& ErrorInConstruct ] )
Private Function ParseCallStatement() As CallStatementSyntax
Debug.Assert(CurrentToken.Kind = SyntaxKind.CallKeyword, "ParseCallStatement called on wrong token.")
Dim callKeyword As KeywordSyntax = DirectCast(CurrentToken, KeywordSyntax)
GetNextToken()
Dim invocation As ExpressionSyntax = MakeCallStatementExpression(ParseVariable())
If invocation.ContainsDiagnostics Then
invocation = ResyncAt(invocation)
End If
Dim statement = SyntaxFactory.CallStatement(callKeyword, invocation)
Return statement
End Function
Private Function MakeCallStatementExpression(expr As ExpressionSyntax) As ExpressionSyntax
If expr.Kind = SyntaxKind.ConditionalAccessExpression Then
Dim conditionalTarget = DirectCast(expr, ConditionalAccessExpressionSyntax)
Dim invocation = MakeCallStatementExpression(conditionalTarget.WhenNotNull)
If conditionalTarget.WhenNotNull IsNot invocation Then
expr = SyntaxFactory.ConditionalAccessExpression(conditionalTarget.Expression, conditionalTarget.QuestionMarkToken, invocation)
End If
ElseIf expr.Kind <> SyntaxKind.InvocationExpression Then
' Make sure that the expression is an invocation in case user left off parentheses
expr = SyntaxFactory.InvocationExpression(expr, Nothing)
End If
Return expr
End Function
' File: Parser.cpp
' Lines: 11656 - 11656
' .Parser::ParseRaiseEventStatement( [ _Inout_ bool& ErrorInConstruct ] )
Private Function ParseRaiseEventStatement() As RaiseEventStatementSyntax
Debug.Assert(CurrentToken.Kind = SyntaxKind.RaiseEventKeyword, "RaiseEvent statement must start with RaiseEvent.")
Dim raiseEventKeyword As KeywordSyntax = DirectCast(CurrentToken, KeywordSyntax)
GetNextToken()
Dim ident = ParseIdentifierNameAllowingKeyword()
If ident.ContainsDiagnostics Then
ident = ident.AddTrailingSyntax(ResyncAt())
End If
Dim optionalArgumentList As ArgumentListSyntax = Nothing
If CurrentToken.Kind = SyntaxKind.OpenParenToken Then
optionalArgumentList = ParseParenthesizedArguments()
End If
Dim statement = SyntaxFactory.RaiseEventStatement(raiseEventKeyword, ident, optionalArgumentList)
Return statement
End Function
' File: Parser.cpp
' Lines: 11690 - 11690
' .Parser::ParseRedimStatement( [ _Inout_ bool& ErrorInConstruct ] )
Private Function ParseRedimStatement() As StatementSyntax '[ReDim]
Debug.Assert(CurrentToken.Kind = SyntaxKind.ReDimKeyword, "ParseRedimStatement must start with Redim.")
Dim reDimKeyword As KeywordSyntax = DirectCast(CurrentToken, KeywordSyntax)
GetNextToken()
Dim possibleKeyword As KeywordSyntax = Nothing
Dim optionalPreserveKeyword As KeywordSyntax = Nothing
If CurrentToken.Kind = SyntaxKind.IdentifierToken AndAlso
TryIdentifierAsContextualKeyword(CurrentToken, possibleKeyword) AndAlso possibleKeyword.Kind = SyntaxKind.PreserveKeyword Then
optionalPreserveKeyword = possibleKeyword
GetNextToken()
End If
' NOTE: Generation of ERR_RedimNoSizes error was moved to binding to match Dev10 behavior
Dim clauses = _pool.AllocateSeparated(Of RedimClauseSyntax)()
Do
Dim possibleInvocation As ExpressionSyntax = ParseTerm(BailIfFirstTokenRejected:=False, RedimOrNewParent:=True)
If possibleInvocation.ContainsDiagnostics Then
possibleInvocation = ResyncAt(possibleInvocation)
End If
Dim clause As RedimClauseSyntax
If possibleInvocation.Kind = SyntaxKind.InvocationExpression Then
Dim invocation = DirectCast(possibleInvocation, InvocationExpressionSyntax)
clause = SyntaxFactory.RedimClause(invocation.Expression, invocation.ArgumentList)
Dim diagnostics() As DiagnosticInfo = invocation.GetDiagnostics()
If diagnostics IsNot Nothing AndAlso diagnostics.Length > 0 Then
clause = clause.WithDiagnostics(diagnostics)
End If
Else
clause = SyntaxFactory.RedimClause(possibleInvocation, SyntaxFactory.ArgumentList(InternalSyntaxFactory.MissingPunctuation(SyntaxKind.OpenParenToken),
Nothing,
InternalSyntaxFactory.MissingPunctuation(SyntaxKind.CloseParenToken)))
End If
clauses.Add(clause)
Dim comma As PunctuationSyntax = Nothing
If Not TryGetTokenAndEatNewLine(SyntaxKind.CommaToken, comma) Then
Exit Do
End If
clauses.AddSeparator(comma)
Loop
Dim statement = If(optionalPreserveKeyword Is Nothing,
SyntaxFactory.ReDimStatement(reDimKeyword, optionalPreserveKeyword, clauses.ToList),
SyntaxFactory.ReDimPreserveStatement(reDimKeyword, optionalPreserveKeyword, clauses.ToList)
)
_pool.Free(clauses)
If CurrentToken.Kind = SyntaxKind.AsKeyword Then
statement = statement.AddTrailingSyntax(CurrentToken, ERRID.ERR_ObsoleteRedimAs)
GetNextToken()
End If
Return statement
End Function
' File: Parser.cpp
' Lines: 11755 - 11755
' .Parser::ParseHandlerStatement( [ _Inout_ bool& ErrorInConstruct ] )
Private Function ParseHandlerStatement() As AddRemoveHandlerStatementSyntax
Debug.Assert(CurrentToken.Kind = SyntaxKind.AddHandlerKeyword OrElse CurrentToken.Kind = SyntaxKind.RemoveHandlerKeyword, "Handler statement parsing confused.")
Dim keyword As KeywordSyntax = DirectCast(CurrentToken, KeywordSyntax)
Dim kind = If(keyword.Kind = SyntaxKind.AddHandlerKeyword, SyntaxKind.AddHandlerStatement, SyntaxKind.RemoveHandlerStatement)
GetNextToken()
Dim eventExpression = ParseExpressionCore()
If eventExpression.ContainsDiagnostics Then
eventExpression = ResyncAt(eventExpression, SyntaxKind.CommaToken)
End If
Dim commaToken As PunctuationSyntax = Nothing
TryGetTokenAndEatNewLine(SyntaxKind.CommaToken, commaToken, createIfMissing:=True)
Dim DelegateExpression = ParseExpressionCore()
If DelegateExpression.ContainsDiagnostics Then
DelegateExpression = ResyncAt(DelegateExpression)
End If
Dim statement = SyntaxFactory.AddRemoveHandlerStatement(kind, keyword, eventExpression, commaToken, DelegateExpression)
Return statement
End Function
' File: Parser.cpp
' Lines: 11830 - 11830
' .Parser::ParseExpressionBlockStatement( [ ParseTree::Statement::Opcodes Opcode ] [ _Inout_ bool& ErrorInConstruct ] )
' davidsch
' used by While/With/Synclock
' The return type can't be more specific because these statements don't share a more
' specific class.
'TODO - Rename ParseKeywordExpression and share with other statements that follow this pattern
' i.e. Throw, others?
Private Function ParseExpressionBlockStatement() As StatementSyntax
Debug.Assert(CurrentToken.Kind = SyntaxKind.WhileKeyword OrElse
CurrentToken.Kind = SyntaxKind.WithKeyword OrElse
CurrentToken.Kind = SyntaxKind.SyncLockKeyword, "ParseExpressionBlockStatement called on wrong token.")
Dim keyword As KeywordSyntax = DirectCast(CurrentToken, KeywordSyntax)
GetNextToken()
Dim operand = ParseExpressionCore(OperatorPrecedence.PrecedenceNone)
If operand.ContainsDiagnostics Then
operand = ResyncAt(operand)
End If
Dim statement As StatementSyntax = Nothing
Select Case keyword.Kind
Case SyntaxKind.WhileKeyword
statement = SyntaxFactory.WhileStatement(keyword, operand)
Case SyntaxKind.WithKeyword
statement = SyntaxFactory.WithStatement(keyword, operand)
Case SyntaxKind.SyncLockKeyword
statement = SyntaxFactory.SyncLockStatement(keyword, operand)
End Select
Return statement
End Function
' File: Parser.cpp
' Lines: 11854 - 11854
' .Parser::ParseAssignmentStatement( [ _Inout_ bool& ErrorInConstruct ] )
'TODO - rename ParseObsoleteAssignment
Private Function ParseAssignmentStatement() As StatementSyntax
Debug.Assert(CurrentToken.Kind = SyntaxKind.LetKeyword OrElse CurrentToken.Kind = SyntaxKind.SetKeyword, "Assignment statement parsing is lost.")
' Let and set are now illegal
If CurrentToken.Kind = SyntaxKind.SetKeyword AndAlso
(IsValidStatementTerminator(PeekToken(1)) OrElse PeekToken(1).Kind = SyntaxKind.OpenParenToken) AndAlso
Context.IsWithin(SyntaxKind.SetAccessorBlock, SyntaxKind.GetAccessorBlock) Then
' If this is a set parse it as a property accessor and then mark it with an error
' so that the Set will terminate a Get context.
Return ParsePropertyOrEventAccessor(SyntaxKind.SetAccessorStatement, Nothing, Nothing)
Else
Dim keyword As SyntaxToken = CurrentToken
GetNextToken()
' Only consume the let. Leave the rest to be processed as an assignment in case the user wrote let x = ...
Return InternalSyntaxFactory.EmptyStatement.AddTrailingSyntax(keyword, ERRID.ERR_ObsoleteLetSetNotNeeded)
End If
End Function
' File: Parser.cpp
' Lines: 11909 - 11909
' .Parser::ParseTry( )
Private Function ParseTry() As TryStatementSyntax
Debug.Assert(CurrentToken.Kind = SyntaxKind.TryKeyword, "ParseTry called on wrong token")
Dim tryKeyword As KeywordSyntax = DirectCast(CurrentToken, KeywordSyntax)
GetNextToken()
Dim statement = SyntaxFactory.TryStatement(tryKeyword)
Return statement
End Function
' File: Parser.cpp
' Lines: 11933 - 11933
' .Parser::ParseCatch( [ _Inout_ bool& ErrorInConstruct ] )
Private Function ParseCatch() As CatchStatementSyntax
Debug.Assert(CurrentToken.Kind = SyntaxKind.CatchKeyword, "ParseCatch called on wrong token.")
Dim catchKeyword As KeywordSyntax = DirectCast(CurrentToken, KeywordSyntax)
GetNextToken()
Dim optionalName As IdentifierNameSyntax = Nothing
Dim optionalAsClause As SimpleAsClauseSyntax = Nothing
If CurrentToken.Kind = SyntaxKind.IdentifierToken Then
Dim id = ParseIdentifier()
If id.Kind <> SyntaxKind.None Then
optionalName = SyntaxFactory.IdentifierName(id)
End If
Dim asKeyword As KeywordSyntax = Nothing
Dim typeName As TypeSyntax = Nothing
If TryGetToken(SyntaxKind.AsKeyword, asKeyword) Then
typeName = ParseTypeName()
If typeName.ContainsDiagnostics Then
typeName = ResyncAt(typeName, SyntaxKind.WhenKeyword)
End If
optionalAsClause = SyntaxFactory.SimpleAsClause(asKeyword, Nothing, typeName)
End If
End If
Dim optionalWhenClause As CatchFilterClauseSyntax = Nothing
Dim whenKeyword As KeywordSyntax = Nothing
Dim filter As ExpressionSyntax = Nothing
If TryGetToken(SyntaxKind.WhenKeyword, whenKeyword) Then
filter = ParseExpressionCore()
optionalWhenClause = SyntaxFactory.CatchFilterClause(whenKeyword, filter)
End If
Dim statement = SyntaxFactory.CatchStatement(catchKeyword, optionalName, optionalAsClause, optionalWhenClause)
Return statement
End Function
' File: Parser.cpp
' Lines: 12022 - 12022
' .Parser::ParseFinally( [ _Inout_ bool& ErrorInConstruct ] )
Private Function ParseFinally() As FinallyStatementSyntax
Debug.Assert(CurrentToken.Kind = SyntaxKind.FinallyKeyword, "ParseFinally called on wrong token.")
Dim finallyKeyword = DirectCast(CurrentToken, KeywordSyntax)
GetNextToken()
Dim statement = SyntaxFactory.FinallyStatement(finallyKeyword)
Return statement
End Function
Private Function ParseThrowStatement() As ThrowStatementSyntax
Debug.Assert(CurrentToken.Kind = SyntaxKind.ThrowKeyword, "ParseThrowStatement called on wrong token.")
Dim throwKeyword = DirectCast(CurrentToken, KeywordSyntax)
GetNextToken()
Dim value = ParseExpressionCore(bailIfFirstTokenRejected:=True)
If value IsNot Nothing Then
If value.ContainsDiagnostics Then
value = ResyncAt(value)
End If
End If
Dim statement = SyntaxFactory.ThrowStatement(throwKeyword, value)
Return statement
End Function
Private Function ParseError() As ErrorStatementSyntax
Debug.Assert(CurrentToken.Kind = SyntaxKind.ErrorKeyword)
Dim errorKeyword = DirectCast(CurrentToken, KeywordSyntax)
GetNextToken()
Dim value = ParseExpressionCore()
If value.ContainsDiagnostics Then
value = ResyncAt(value)
End If
Dim statement = SyntaxFactory.ErrorStatement(errorKeyword, value)
Return statement
End Function
' Parse an Erase statement.
' File: Parser.cpp
' Lines: 12077 - 12077
' .Parser::ParseErase( [ _Inout_ bool& ErrorInConstruct ] )
Private Function ParseErase() As EraseStatementSyntax
Debug.Assert(CurrentToken.Kind = SyntaxKind.EraseKeyword, "Erase statement parsing lost.")
Dim eraseKeyword As KeywordSyntax = DirectCast(CurrentToken, KeywordSyntax)
GetNextToken()
Dim variables = ParseVariableList()
Dim statement = SyntaxFactory.EraseStatement(eraseKeyword, variables)
Return statement
End Function
Private Function ShouldParseAsLabel() As Boolean
Return IsFirstStatementOnLine(CurrentToken) AndAlso PeekToken(1).Kind = SyntaxKind.ColonToken
End Function
Private Function ParseLabel() As LabelStatementSyntax
Debug.Assert(CurrentToken.Kind = SyntaxKind.IdentifierToken OrElse CurrentToken.Kind = SyntaxKind.IntegerLiteralToken)
Dim labelName = ParseLabelReference()
If labelName.Kind = SyntaxKind.IntegerLiteralToken AndAlso CurrentToken.Kind <> SyntaxKind.ColonToken Then
Return ReportSyntaxError(SyntaxFactory.LabelStatement(labelName, InternalSyntaxFactory.MissingPunctuation(SyntaxKind.ColonToken)), ERRID.ERR_ObsoleteLineNumbersAreLabels)
End If
Dim trivia = New CodeAnalysis.Syntax.InternalSyntax.SyntaxList(Of VisualBasicSyntaxNode)(labelName.GetTrailingTrivia())
Debug.Assert(trivia.Count > 0)
Dim index = -1
For i = 0 To trivia.Count - 1
If trivia(i).Kind = SyntaxKind.ColonTrivia Then
index = i
Exit For
End If
Next
Debug.Assert(index >= 0)
labelName = DirectCast(labelName.WithTrailingTrivia(trivia.GetStartOfTrivia(index).Node), SyntaxToken)
Dim colonTrivia = DirectCast(trivia(index), SyntaxTrivia)
trivia = trivia.GetEndOfTrivia(index + 1)
Dim colonToken = New PunctuationSyntax(SyntaxKind.ColonToken, colonTrivia.Text, Nothing, trivia.Node)
Dim statement = SyntaxFactory.LabelStatement(labelName, colonToken)
Return statement
End Function
' Parse a Mid statement.
' File: Parser.cpp
' Lines: 12105 - 12105
' .Parser::ParseMid( [ _Inout_ bool& ErrorInConstruct ] )
Private Function ParseMid() As AssignmentStatementSyntax
Debug.Assert(CurrentToken.Kind = SyntaxKind.IdentifierToken AndAlso DirectCast(CurrentToken, IdentifierTokenSyntax).PossibleKeywordKind = SyntaxKind.MidKeyword)
' Mid[$] OpenParenthesis Expression Comma Expression [ Comma Expression ] CloseParenthesis Equals Expression StatementTerminator
'TODO - Does the Mid need to be modeled as a contextual keyword? Right now it is kept as an identifier.
Dim mid = DirectCast(CurrentToken, IdentifierTokenSyntax)
GetNextToken()
Debug.Assert(CurrentToken.Kind = SyntaxKind.OpenParenToken)
Dim openParen As PunctuationSyntax = Nothing
TryGetTokenAndEatNewLine(SyntaxKind.OpenParenToken, openParen)
Dim argumentsBuilder As SeparatedSyntaxListBuilder(Of ArgumentSyntax) = _pool.AllocateSeparated(Of ArgumentSyntax)()
Dim comma As PunctuationSyntax = Nothing
' Parse the first required argument followed by a comma
argumentsBuilder.Add(ParseArgument(RedimOrNewParent:=False))
If Not TryGetTokenAndEatNewLine(SyntaxKind.CommaToken, comma) Then
VerifyExpectedToken(SyntaxKind.CommaToken, comma)
End If
argumentsBuilder.AddSeparator(comma)
' Parse the second required argument
argumentsBuilder.Add(ParseArgument(RedimOrNewParent:=False))
If TryGetTokenAndEatNewLine(SyntaxKind.CommaToken, comma) Then
argumentsBuilder.AddSeparator(comma)
' Parse the third argument
argumentsBuilder.Add(ParseArgument(RedimOrNewParent:=False))
End If
Dim arguments As CodeAnalysis.Syntax.InternalSyntax.SeparatedSyntaxList(Of ArgumentSyntax) = argumentsBuilder.ToList
_pool.Free(argumentsBuilder)
Dim closeParen As PunctuationSyntax = Nothing
TryEatNewLineAndGetToken(SyntaxKind.CloseParenToken, closeParen, createIfMissing:=True)
Dim equals As PunctuationSyntax = Nothing
VerifyExpectedToken(SyntaxKind.EqualsToken, equals)
Dim source As ExpressionSyntax = ParseExpressionCore()
If source.ContainsDiagnostics() Then
source = ResyncAt(source)
End If
Dim statement = SyntaxFactory.MidAssignmentStatement(SyntaxFactory.MidExpression(mid, SyntaxFactory.ArgumentList(openParen, arguments, closeParen)),
equals, source)
Return statement
End Function
' Out
' File: Parser.cpp
' Lines: 16887 - 16887
' Expression* .Parser::ParseOptionalWhileOrUntilClause( [ _Out_ bool* IsWhile ] [ _Inout_ bool& ErrorInConstruct ] )
Private Function TryParseOptionalWhileOrUntilClause(precedingKeyword As KeywordSyntax, ByRef optionalWhileOrUntilClause As WhileOrUntilClauseSyntax) As Boolean
If Not CanFollowStatement(CurrentToken) Then
Dim keyword As KeywordSyntax = Nothing
If CurrentToken.Kind = SyntaxKind.WhileKeyword Then
keyword = DirectCast(CurrentToken, KeywordSyntax)
Else
TryTokenAsContextualKeyword(CurrentToken, SyntaxKind.UntilKeyword, keyword)
End If
If keyword IsNot Nothing Then
' Error reporting for ERRID.ERR_LoopDoubleCondition moved to the DoLoopContext
GetNextToken()
Dim condition = ParseExpressionCore()
If condition.ContainsDiagnostics Then
condition = ResyncAt(condition)
End If
Dim kind As SyntaxKind
If keyword.Kind = SyntaxKind.WhileKeyword Then
kind = SyntaxKind.WhileClause
Else
kind = SyntaxKind.UntilClause
End If
optionalWhileOrUntilClause = SyntaxFactory.WhileOrUntilClause(kind, keyword, condition)
Return True
Else
Dim kind As SyntaxKind
If precedingKeyword.Kind = SyntaxKind.DoKeyword Then
kind = SyntaxKind.UntilClause
keyword = InternalSyntaxFactory.MissingKeyword(SyntaxKind.UntilKeyword)
Else
kind = SyntaxKind.WhileClause
keyword = InternalSyntaxFactory.MissingKeyword(SyntaxKind.WhileKeyword)
End If
Dim clause As WhileOrUntilClauseSyntax = SyntaxFactory.WhileOrUntilClause(kind, keyword, InternalSyntaxFactory.MissingExpression)
' Dev10 places the error only on the current token.
' This marks the entire clause which seems better.
optionalWhileOrUntilClause = ReportSyntaxError(ResyncAt(clause), ERRID.ERR_Syntax)
Return True
End If
End If
Return False
End Function
Private Function ParseReturnStatement() As ReturnStatementSyntax
Debug.Assert(CurrentToken.Kind = SyntaxKind.ReturnKeyword, "ParseReturnStatement called on wrong token.")
Dim returnKeyword As KeywordSyntax = DirectCast(CurrentToken, KeywordSyntax)
GetNextToken()
Dim startToken = CurrentToken
' Dev10#694102 - Consider "Dim f = Sub() Return + 5". Which of the following should it mean?
' Dim f = (Sub() Return) + 5 ' use an overloaded addition operator
' Dim f = (Sub() Return (+5)) ' return the number +5
' The spec says that we will greedily parse the body of a statement lambda.
' And indeed doing so agrees with the user's intuition ("Return +5" should give an error
' that subs cannot return values: it should not give an error that there is no overloaded
' operator + between functions and integers!)
' We will try to parse the expression, but the final "true" argument means
' "Bail if the first token isn't a valid way to start an expression; in this case just return NULL"
Dim operand = ParseExpressionCore(OperatorPrecedence.PrecedenceNone, True)
' Note: Orcas behavior had been to bail immediately if IsValidStatementTerminator(CurrentToken).
' Well, all such tokens are invalid as ways to start an expression, so the above call to ParseExpressionCore
' will bail correctly for them. I've put in this assert to show that it's safe to skip the check for IsValidStatementTerminator.
Debug.Assert(operand Is Nothing OrElse Not IsValidStatementTerminator(startToken), "Unexpected: we should have bailed on the token after this return statement")
If operand Is Nothing Then
' if we bailed because the first token was not a way to start an expression, we might
' be in a situation like "goo(Sub() Return, 15)" where next token was a valid thing
' to come after this return statement, in which case we proceed without trying
' to gobble up the return expression. Or we might be like "Return Select", where the next
' token cannot possibly come after the statement, so we'll report on it now:
If Not CanFollowStatement(CurrentToken) Then
' This time don't let it bail:
operand = ParseExpressionCore(OperatorPrecedence.PrecedenceNone, False)
End If
ElseIf operand.ContainsDiagnostics Then
operand = ResyncAt(operand)
End If
Dim statement = SyntaxFactory.ReturnStatement(returnKeyword, operand)
Return statement
End Function
'TODO - See if all of the simple statements that take only one keyword can be merged into one parsing function
' StopOrEnd
' TryStatement
' FinallyStatement
Private Function ParseStopOrEndStatement() As StopOrEndStatementSyntax
Debug.Assert(CurrentToken.Kind = SyntaxKind.StopKeyword OrElse CurrentToken.Kind = SyntaxKind.EndKeyword, "ParseStopOrEndStatement called on wrong token.")
Dim stopOrEndKeyword As KeywordSyntax = DirectCast(CurrentToken, KeywordSyntax)
GetNextToken()
Dim stmtKind As SyntaxKind = If(stopOrEndKeyword.Kind = SyntaxKind.StopKeyword, SyntaxKind.StopStatement, SyntaxKind.EndStatement)
Dim statement = SyntaxFactory.StopOrEndStatement(stmtKind, stopOrEndKeyword)
Return statement
End Function
Private Function ParseUsingStatement() As UsingStatementSyntax
Debug.Assert(CurrentToken.Kind = SyntaxKind.UsingKeyword, "ParseUsingStatement called on wrong token")
Dim usingKeyword As KeywordSyntax = DirectCast(CurrentToken, KeywordSyntax)
GetNextToken()
Dim optionalExpression As ExpressionSyntax = Nothing
Dim variables As CodeAnalysis.Syntax.InternalSyntax.SeparatedSyntaxList(Of VariableDeclaratorSyntax) = Nothing
Dim nextToken As SyntaxToken = PeekToken(1)
' change from Dev10: allowing as new with multiple variable names, e.g. "Using a, b As New C1()"
If nextToken.Kind = SyntaxKind.AsKeyword OrElse
nextToken.Kind = SyntaxKind.EqualsToken OrElse
nextToken.Kind = SyntaxKind.CommaToken OrElse
nextToken.Kind = SyntaxKind.QuestionToken Then
variables = ParseVariableDeclaration(allowAsNewWith:=True)
Else
optionalExpression = ParseExpressionCore()
End If
'TODO - not resyncing here may cause errors to differ from Dev10.
'No need to resync on error. This will be handled by GetStatementTerminator
Dim statement = SyntaxFactory.UsingStatement(usingKeyword, optionalExpression, variables)
Return statement
End Function
Private Function ParseAwaitStatement() As ExpressionStatementSyntax
Debug.Assert(CurrentToken.Kind = SyntaxKind.IdentifierToken AndAlso
DirectCast(CurrentToken, IdentifierTokenSyntax).ContextualKind = SyntaxKind.AwaitKeyword,
"ParseAwaitStatement called on wrong token.")
Dim expression = ParseAwaitExpression()
Debug.Assert(expression.Kind = SyntaxKind.AwaitExpression)
If expression.ContainsDiagnostics Then
expression = ResyncAt(expression)
End If
Dim statement = SyntaxFactory.ExpressionStatement(expression)
Return statement
End Function
Private Function ParseYieldStatement() As YieldStatementSyntax
Debug.Assert(DirectCast(CurrentToken, IdentifierTokenSyntax).ContextualKind = SyntaxKind.YieldKeyword)
Dim yieldKeyword As KeywordSyntax = Nothing
TryIdentifierAsContextualKeyword(CurrentToken, yieldKeyword)
Debug.Assert(yieldKeyword IsNot Nothing AndAlso yieldKeyword.Kind = SyntaxKind.YieldKeyword)
yieldKeyword = CheckFeatureAvailability(Feature.Iterators, yieldKeyword)
GetNextToken()
Dim expression As ExpressionSyntax = ParseExpressionCore()
Dim result = SyntaxFactory.YieldStatement(yieldKeyword, expression)
Return result
End Function
Private Function ParsePrintStatement() As PrintStatementSyntax
Dim questionToken = DirectCast(CurrentToken, PunctuationSyntax)
GetNextToken()
Dim expression As ExpressionSyntax = ParseExpressionCore()
Dim result = SyntaxFactory.PrintStatement(questionToken, expression)
' skip possible statement terminator
Dim lookahead = PeekToken(1)
If lookahead.Kind <> SyntaxKind.EndOfFileToken OrElse _scanner.Options.Kind = SourceCodeKind.Regular Then
result = result.AddError(ERRID.ERR_UnexpectedExpressionStatement)
End If
Return result
End Function
End Class
End Namespace
|