/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/VisualBasic/Portable/Parser/ParseQuery.vb
1 301 строка
62 KB
Julien Couvreur
Warn for multiple blank lines in source (#65451)
17 ноя 2022, 22:55
Не верифицирован
17 ноя 2022, 22:55
a8824e8
Код
Авторство
О чём код?
' Licensed to the .NET Foundation under one or more agreements. ' The .NET Foundation licenses this file to you under the MIT license. ' See the LICENSE file in the project root for more information. Imports Microsoft.CodeAnalysis.Syntax.InternalSyntax Imports InternalSyntaxFactory = Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax.SyntaxFactory Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax Partial Friend Class Parser ' File: Parser.cpp ' Lines: 14199 - 14199 ' Initializer* .Parser::ParseSelectListInitializer( [ _Inout_ bool& ErrorInConstruct ] ) Private Function ParseSelectListInitializer() As ExpressionRangeVariableSyntax Dim nameEqualsOpt As VariableNameEqualsSyntax = Nothing If ((CurrentToken.Kind = SyntaxKind.IdentifierToken OrElse CurrentToken.IsKeyword()) AndAlso PeekToken(1).Kind = SyntaxKind.EqualsToken OrElse (PeekToken(1).Kind = SyntaxKind.QuestionToken AndAlso PeekToken(2).Kind = SyntaxKind.EqualsToken)) Then Dim varName As ModifiedIdentifierSyntax = Nothing Dim Equals As PunctuationSyntax = Nothing ' // Parse form: <IdentifierOrKeyword> '=' <Expression> varName = ParseSimpleIdentifierAsModifiedIdentifier() ' NOTE: do not need to resync here. we should land on "=" Debug.Assert(CurrentToken.Kind = SyntaxKind.EqualsToken) Equals = DirectCast(CurrentToken, PunctuationSyntax) GetNextToken() '// move off the '=' TryEatNewLine() ' // line continuation allowed after '=' nameEqualsOpt = SyntaxFactory.VariableNameEquals(varName, Nothing, Equals) End If Dim expr As ExpressionSyntax = ParseExpressionCore() Return SyntaxFactory.ExpressionRangeVariable( nameEqualsOpt, expr) End Function ' File: Parser.cpp ' Lines: 14290 - 14290 ' InitializerList* .Parser::ParseSelectList( [ _Inout_ bool& ErrorInConstruct ] ) Private Function ParseSelectList() As CodeAnalysis.Syntax.InternalSyntax.SeparatedSyntaxList(Of ExpressionRangeVariableSyntax) Dim RangeVariables = Me._pool.AllocateSeparated(Of ExpressionRangeVariableSyntax)() Do Dim rangeVar = ParseSelectListInitializer() If rangeVar.ContainsDiagnostics Then rangeVar = ResyncAt(rangeVar, SyntaxKind.CommaToken, SyntaxKind.WhereKeyword, SyntaxKind.GroupKeyword, SyntaxKind.SelectKeyword, SyntaxKind.OrderKeyword, SyntaxKind.JoinKeyword, SyntaxKind.FromKeyword, SyntaxKind.DistinctKeyword, SyntaxKind.AggregateKeyword, SyntaxKind.IntoKeyword, SyntaxKind.SkipKeyword, SyntaxKind.TakeKeyword, SyntaxKind.LetKeyword) End If RangeVariables.Add(rangeVar) If CurrentToken.Kind = SyntaxKind.CommaToken Then Dim comma = DirectCast(CurrentToken, PunctuationSyntax) GetNextToken() TryEatNewLine() RangeVariables.AddSeparator(comma) Else Exit Do End If Loop Dim result = RangeVariables.ToList Me._pool.Free(RangeVariables) Return result End Function ' File: Parser.cpp ' Lines: 14333 - 14333 ' Expression* .Parser::ParseAggregationExpression( [ _Inout_ bool& ErrorInConstruct ] ) Private Function ParseAggregationExpression() As AggregationSyntax If CurrentToken.Kind = SyntaxKind.IdentifierToken Then Dim curIdent = DirectCast(CurrentToken, IdentifierTokenSyntax) If curIdent.PossibleKeywordKind = SyntaxKind.GroupKeyword Then Debug.Assert(PeekToken(1).Kind = SyntaxKind.OpenParenToken) curIdent = ReportSyntaxError(curIdent, ERRID.ERR_InvalidUseOfKeyword) GetNextToken() Return SyntaxFactory.FunctionAggregation(curIdent, Nothing, Nothing, Nothing) End If End If Dim aggName = ParseIdentifier() Dim aggregateFunc As FunctionAggregationSyntax = Nothing If Not aggName.ContainsDiagnostics AndAlso CurrentToken.Kind = SyntaxKind.OpenParenToken Then Dim lParen = DirectCast(CurrentToken, PunctuationSyntax) GetNextToken() ' get off lparen TryEatNewLine() Dim arg As ExpressionSyntax = Nothing If CurrentToken.Kind <> SyntaxKind.CloseParenToken Then arg = ParseExpressionCore() End If Dim rParen As PunctuationSyntax = Nothing If TryEatNewLineAndGetToken(SyntaxKind.CloseParenToken, rParen, createIfMissing:=True) Then ' // check that expression doesn't continue If arg IsNot Nothing Then CheckForEndOfExpression(arg) End If End If aggregateFunc = SyntaxFactory.FunctionAggregation(aggName, lParen, arg, rParen) Else aggregateFunc = SyntaxFactory.FunctionAggregation(aggName, Nothing, Nothing, Nothing) ' // check that expression doesn't continue CheckForEndOfExpression(aggregateFunc) End If Return aggregateFunc End Function ' // check that expression doesn't continue ' File: Parser.cpp ' Lines: 14420 - 14420 ' bool .Parser::CheckForEndOfExpression( [ Token* Start ] [ bool& ErrorInConstruct ] ) Private Function CheckForEndOfExpression(Of T As VisualBasicSyntaxNode)(ByRef syntax As T) As Boolean Debug.Assert(syntax IsNot Nothing) 'TODO: this is very different from original implementation. ' originally we would try to parse the whole thing as an expression and see if we ' will not consume more than "syntax". It seems that "syntax" is always a term ' so the only way ParseExpressionCore could consume more is if there is a binary operator. ' Hopefully checking for binary operator is enough... If Not CurrentToken.IsBinaryOperator AndAlso CurrentToken.Kind <> SyntaxKind.DotToken Then Return True End If syntax = ReportSyntaxError(syntax, ERRID.ERR_ExpectedEndOfExpression) Return False End Function ' Several places the syntax trees allow a modified identifier, but we actually don't want to ' allow a trailing ?. This function encapsulates that. Private Function ParseSimpleIdentifierAsModifiedIdentifier() As ModifiedIdentifierSyntax Dim varName As IdentifierTokenSyntax = ParseIdentifier() If CurrentToken.Kind = SyntaxKind.QuestionToken Then Dim unexpectedNullable As SyntaxToken = CurrentToken varName = varName.AddTrailingSyntax(ReportSyntaxError(unexpectedNullable, ERRID.ERR_NullableTypeInferenceNotSupported)) GetNextToken() ' get off the "?" End If Return SyntaxFactory.ModifiedIdentifier(varName, Nothing, Nothing, Nothing) End Function ' File: Parser.cpp ' Lines: 14447 - 14447 ' Initializer* .Parser::ParseAggregateListInitializer( [ bool AllowGroupName ] [ _Inout_ bool& ErrorInConstruct ] ) Private Function ParseAggregateListInitializer(AllowGroupName As Boolean) As AggregationRangeVariableSyntax Dim varName As ModifiedIdentifierSyntax = Nothing Dim Equals As PunctuationSyntax = Nothing If ((CurrentToken.Kind = SyntaxKind.IdentifierToken OrElse CurrentToken.IsKeyword()) AndAlso PeekToken(1).Kind = SyntaxKind.EqualsToken _ OrElse (PeekToken(1).Kind = SyntaxKind.QuestionToken AndAlso PeekToken(2).Kind = SyntaxKind.EqualsToken)) Then ' // Parse form: <IdentifierOrKeyword> '=' <Expression> varName = ParseSimpleIdentifierAsModifiedIdentifier() ' NOTE: do not need to resync here. we should land on "=" Debug.Assert(CurrentToken.Kind = SyntaxKind.EqualsToken) Equals = DirectCast(CurrentToken, PunctuationSyntax) GetNextToken() '// move off the '=' TryEatNewLine() ' // line continuation allowed after '=' End If Dim expr As AggregationSyntax = Nothing If CurrentToken.Kind = SyntaxKind.IdentifierToken OrElse CurrentToken.IsKeyword() Then Dim groupKw As KeywordSyntax = Nothing If TryTokenAsContextualKeyword(CurrentToken, SyntaxKind.GroupKeyword, groupKw) AndAlso PeekToken(1).Kind <> SyntaxKind.OpenParenToken Then If Not AllowGroupName Then groupKw = ReportSyntaxError(groupKw, ERRID.ERR_UnexpectedGroup) End If expr = SyntaxFactory.GroupAggregation(groupKw) ' // check that expression doesn't continue CheckForEndOfExpression(expr) GetNextToken() ' // Move off 'Group Else expr = ParseAggregationExpression() ' // this must be an Aggregation End If Else Dim missingIdent = InternalSyntaxFactory.MissingIdentifier() If AllowGroupName Then missingIdent = ReportSyntaxError(missingIdent, ERRID.ERR_ExpectedIdentifierOrGroup) Else missingIdent = ReportSyntaxError(missingIdent, ERRID.ERR_ExpectedIdentifier) End If expr = SyntaxFactory.FunctionAggregation(missingIdent, Nothing, Nothing, Nothing) End If Debug.Assert((varName Is Nothing) = (Equals Is Nothing)) Dim variableNameEquals As VariableNameEqualsSyntax = Nothing If varName IsNot Nothing AndAlso Equals IsNot Nothing Then variableNameEquals = SyntaxFactory.VariableNameEquals(varName, Nothing, Equals) End If Return SyntaxFactory.AggregationRangeVariable( variableNameEquals, expr) End Function ' File: Parser.cpp ' Lines: 14615 - 14615 ' InitializerList* .Parser::ParseAggregateList( [ bool AllowGroupName ] [ bool IsGroupJoinProjection ] [ _Inout_ bool& ErrorInConstruct ] ) Private Function ParseAggregateList( AllowGroupName As Boolean, IsGroupJoinProjection As Boolean) As CodeAnalysis.Syntax.InternalSyntax.SeparatedSyntaxList(Of AggregationRangeVariableSyntax) Dim RangeVariables = Me._pool.AllocateSeparated(Of AggregationRangeVariableSyntax)() Do Dim rangeVar = ParseAggregateListInitializer(AllowGroupName) If rangeVar.ContainsDiagnostics Then If IsGroupJoinProjection Then rangeVar = ResyncAt(rangeVar, SyntaxKind.CommaToken, SyntaxKind.WhereKeyword, SyntaxKind.GroupKeyword, SyntaxKind.SelectKeyword, SyntaxKind.OrderKeyword, SyntaxKind.JoinKeyword, SyntaxKind.FromKeyword, SyntaxKind.DistinctKeyword, SyntaxKind.AggregateKeyword, SyntaxKind.IntoKeyword, SyntaxKind.OnKeyword, SyntaxKind.SkipKeyword, SyntaxKind.TakeKeyword, SyntaxKind.LetKeyword) Else rangeVar = ResyncAt(rangeVar, SyntaxKind.CommaToken, SyntaxKind.WhereKeyword, SyntaxKind.GroupKeyword, SyntaxKind.SelectKeyword, SyntaxKind.OrderKeyword, SyntaxKind.JoinKeyword, SyntaxKind.FromKeyword, SyntaxKind.DistinctKeyword, SyntaxKind.AggregateKeyword, SyntaxKind.IntoKeyword, SyntaxKind.SkipKeyword, SyntaxKind.TakeKeyword, SyntaxKind.LetKeyword) End If End If RangeVariables.Add(rangeVar) If CurrentToken.Kind = SyntaxKind.CommaToken Then Dim comma = DirectCast(CurrentToken, PunctuationSyntax) GetNextToken() TryEatNewLine() RangeVariables.AddSeparator(comma) Else Exit Do End If Loop Dim result = RangeVariables.ToList Me._pool.Free(RangeVariables) Return result End Function ' File: Parser.cpp ' Lines: 14666 - 14666 ' FromList* .Parser::ParseFromList( [ bool AssignmentList ] [ _Inout_ bool& ErrorInConstruct ] ) ' TODO: Merge with ParseFromControlVars. The two methods are almost identical. Private Function ParseLetList() As CodeAnalysis.Syntax.InternalSyntax.SeparatedSyntaxList(Of ExpressionRangeVariableSyntax) Dim RangeVariables = Me._pool.AllocateSeparated(Of ExpressionRangeVariableSyntax)() Do Dim varName As ModifiedIdentifierSyntax = ParseNullableModifiedIdentifier() If varName.ContainsDiagnostics Then ' // If we see As or In before other query operators, then assume that ' // we are still on the Control Variable Declaration. ' // Otherwise, don't resync and allow the caller to ' // decide how to recover. Dim peek = PeekAheadFor(SyntaxKind.AsKeyword, SyntaxKind.InKeyword, SyntaxKind.CommaToken, SyntaxKind.FromKeyword, SyntaxKind.WhereKeyword, SyntaxKind.GroupKeyword, SyntaxKind.SelectKeyword, SyntaxKind.OrderKeyword, SyntaxKind.JoinKeyword, SyntaxKind.DistinctKeyword, SyntaxKind.AggregateKeyword, SyntaxKind.IntoKeyword, SyntaxKind.SkipKeyword, SyntaxKind.TakeKeyword, SyntaxKind.LetKeyword) Select Case (peek) Case SyntaxKind.AsKeyword, SyntaxKind.InKeyword, SyntaxKind.CommaToken varName = varName.AddTrailingSyntax(ResyncAt({peek})) End Select End If If CurrentToken.Kind = SyntaxKind.QuestionToken AndAlso (PeekToken(1).Kind = SyntaxKind.InKeyword OrElse PeekToken(1).Kind = SyntaxKind.EqualsToken) Then Dim unexpectedNullable As SyntaxToken = CurrentToken varName = varName.AddTrailingSyntax(ReportSyntaxError(unexpectedNullable, ERRID.ERR_NullableTypeInferenceNotSupported)) GetNextToken() ' get off the "?" End If Dim AsClause As SimpleAsClauseSyntax = Nothing Dim TokenFollowingAsWasIn As Boolean = False ' // Parse the type if specified If CurrentToken.Kind = SyntaxKind.AsKeyword Then Dim AsKw As KeywordSyntax = DirectCast(CurrentToken, KeywordSyntax) Debug.Assert(AsKw IsNot Nothing) ' // Parse the type GetNextToken() ' // get off AS If CurrentToken.Kind = SyntaxKind.InKeyword Then TokenFollowingAsWasIn = True End If Dim Type As TypeSyntax = ParseGeneralType() AsClause = SyntaxFactory.SimpleAsClause(AsKw, Nothing, Type) ' // try to recover If Type.ContainsDiagnostics Then Dim peek = PeekAheadFor(SyntaxKind.InKeyword, SyntaxKind.CommaToken, SyntaxKind.EqualsToken, SyntaxKind.FromKeyword, SyntaxKind.WhereKeyword, SyntaxKind.GroupKeyword, SyntaxKind.SelectKeyword, SyntaxKind.OrderKeyword, SyntaxKind.JoinKeyword, SyntaxKind.DistinctKeyword, SyntaxKind.AggregateKeyword, SyntaxKind.IntoKeyword, SyntaxKind.SkipKeyword, SyntaxKind.TakeKeyword, SyntaxKind.LetKeyword) Select Case (peek) Case SyntaxKind.AsKeyword, SyntaxKind.InKeyword, SyntaxKind.CommaToken AsClause = AsClause.AddTrailingSyntax(ResyncAt({peek})) End Select End If End If Dim Equals As PunctuationSyntax = Nothing Dim source As ExpressionSyntax = Nothing If Not TryGetToken(SyntaxKind.EqualsToken, Equals) Then Equals = InternalSyntaxFactory.MissingPunctuation(SyntaxKind.EqualsToken) Equals = ReportSyntaxError(Equals, ERRID.ERR_ExpectedAssignmentOperator) Else If Not TokenFollowingAsWasIn Then TryEatNewLine() ' // enable implicit LC after 'In' or '=' But not if somebody did from x as IN End If source = ParseExpressionCore() End If ' // try to recover If source Is Nothing OrElse source.ContainsDiagnostics Then ' Fix up source source = If(source, InternalSyntaxFactory.MissingExpression) Dim peek = PeekAheadFor(SyntaxKind.CommaToken, SyntaxKind.FromKeyword, SyntaxKind.WhereKeyword, SyntaxKind.GroupKeyword, SyntaxKind.SelectKeyword, SyntaxKind.OrderKeyword, SyntaxKind.JoinKeyword, SyntaxKind.DistinctKeyword, SyntaxKind.AggregateKeyword, SyntaxKind.IntoKeyword, SyntaxKind.SkipKeyword, SyntaxKind.TakeKeyword, SyntaxKind.LetKeyword) If peek = SyntaxKind.CommaToken Then source = source.AddTrailingSyntax(ResyncAt({peek})) End If End If Dim rangeVar = SyntaxFactory.ExpressionRangeVariable( SyntaxFactory.VariableNameEquals(varName, AsClause, Equals), source) RangeVariables.Add(rangeVar) ' // check for list continuation Dim comma As PunctuationSyntax = Nothing If TryGetTokenAndEatNewLine(SyntaxKind.CommaToken, comma) Then RangeVariables.AddSeparator(comma) Else Exit Do End If Loop Dim result = RangeVariables.ToList Me._pool.Free(RangeVariables) Return result End Function Private Function ParseFromControlVars() As CodeAnalysis.Syntax.InternalSyntax.SeparatedSyntaxList(Of CollectionRangeVariableSyntax) Dim RangeVariables = Me._pool.AllocateSeparated(Of CollectionRangeVariableSyntax)() Do Dim varName As ModifiedIdentifierSyntax = ParseNullableModifiedIdentifier() If varName.ContainsDiagnostics Then ' // If we see As or In before other query operators, then assume that ' // we are still on the Control Variable Declaration. ' // Otherwise, don't resync and allow the caller to ' // decide how to recover. Dim peek = PeekAheadFor(SyntaxKind.AsKeyword, SyntaxKind.InKeyword, SyntaxKind.CommaToken, SyntaxKind.FromKeyword, SyntaxKind.WhereKeyword, SyntaxKind.GroupKeyword, SyntaxKind.SelectKeyword, SyntaxKind.OrderKeyword, SyntaxKind.JoinKeyword, SyntaxKind.DistinctKeyword, SyntaxKind.AggregateKeyword, SyntaxKind.IntoKeyword, SyntaxKind.SkipKeyword, SyntaxKind.TakeKeyword, SyntaxKind.LetKeyword) Select Case (peek) Case SyntaxKind.AsKeyword, SyntaxKind.InKeyword, SyntaxKind.CommaToken varName = varName.AddTrailingSyntax(ResyncAt({peek})) End Select End If If CurrentToken.Kind = SyntaxKind.QuestionToken AndAlso (PeekToken(1).Kind = SyntaxKind.InKeyword OrElse PeekToken(1).Kind = SyntaxKind.EqualsToken) Then Dim unexpectedNullable As SyntaxToken = CurrentToken varName = varName.AddTrailingSyntax(ReportSyntaxError(unexpectedNullable, ERRID.ERR_NullableTypeInferenceNotSupported)) GetNextToken() ' get off the "?" End If Dim AsClause As SimpleAsClauseSyntax = Nothing Dim TokenFollowingAsWasIn As Boolean = False ' // Parse the type if specified If CurrentToken.Kind = SyntaxKind.AsKeyword Then Dim AsKw As KeywordSyntax = DirectCast(CurrentToken, KeywordSyntax) Debug.Assert(AsKw IsNot Nothing) ' // Parse the type GetNextToken() ' // get off AS If CurrentToken.Kind = SyntaxKind.InKeyword Then TokenFollowingAsWasIn = True End If Dim Type As TypeSyntax = ParseGeneralType() AsClause = SyntaxFactory.SimpleAsClause(AsKw, Nothing, Type) ' // try to recover If Type.ContainsDiagnostics Then Dim peek = PeekAheadFor(SyntaxKind.InKeyword, SyntaxKind.CommaToken, SyntaxKind.EqualsToken, SyntaxKind.FromKeyword, SyntaxKind.WhereKeyword, SyntaxKind.GroupKeyword, SyntaxKind.SelectKeyword, SyntaxKind.OrderKeyword, SyntaxKind.JoinKeyword, SyntaxKind.DistinctKeyword, SyntaxKind.AggregateKeyword, SyntaxKind.IntoKeyword, SyntaxKind.SkipKeyword, SyntaxKind.TakeKeyword, SyntaxKind.LetKeyword) Select Case (peek) Case SyntaxKind.AsKeyword, SyntaxKind.InKeyword, SyntaxKind.CommaToken AsClause = AsClause.AddTrailingSyntax(ResyncAt({peek})) End Select End If End If Dim [In] As KeywordSyntax = Nothing Dim source As ExpressionSyntax = Nothing If TryEatNewLineAndGetToken(SyntaxKind.InKeyword, [In], createIfMissing:=True) Then If Not TokenFollowingAsWasIn Then TryEatNewLine() ' // enable implicit LC after 'In' or '=' But not if somebody did from x as IN End If source = ParseExpressionCore() End If ' // try to recover If source Is Nothing OrElse source.ContainsDiagnostics Then ' Fix up source source = If(source, InternalSyntaxFactory.MissingExpression) ' resync Dim peek = PeekAheadFor(SyntaxKind.CommaToken, SyntaxKind.FromKeyword, SyntaxKind.WhereKeyword, SyntaxKind.GroupKeyword, SyntaxKind.SelectKeyword, SyntaxKind.OrderKeyword, SyntaxKind.JoinKeyword, SyntaxKind.DistinctKeyword, SyntaxKind.AggregateKeyword, SyntaxKind.IntoKeyword, SyntaxKind.SkipKeyword, SyntaxKind.TakeKeyword, SyntaxKind.LetKeyword) If peek = SyntaxKind.CommaToken Then source = source.AddTrailingSyntax(ResyncAt({peek})) End If End If Dim rangeVar = SyntaxFactory.CollectionRangeVariable(varName, AsClause, [In], source) RangeVariables.Add(rangeVar) ' // check for list continuation Dim comma As PunctuationSyntax = Nothing If TryGetTokenAndEatNewLine(SyntaxKind.CommaToken, comma) Then RangeVariables.AddSeparator(comma) Else Exit Do End If Loop Dim result = RangeVariables.ToList Me._pool.Free(RangeVariables) Return result End Function ' /********************************************************************* ' ;ParsePotentialQuery ' ' The parser determined that we might be on a query expression because we were on 'From' or ' 'Aggregate' We now see if we actually are on a query expression and parse it if we are. ' **********************************************************************/ ' // the query expression if this is in fact a query we are on ' // [out] true = that we were on a query and that we handled it / false = not a query ' // [out] whether we encountered an error processing the query statement (assuming this actually is a query statement) ' Expression* .Parser::ParsePotentialQuery( [ _Inout_ ParseTree::LineContinuationState& continuationState ] [ _Out_ bool& ParsedQuery ] [ _Out_ bool& ErrorInConstruct ] ) Private Function ParsePotentialQuery(contextualKeyword As KeywordSyntax) As ExpressionSyntax Debug.Assert(contextualKeyword IsNot Nothing) Debug.Assert(contextualKeyword.Kind = SyntaxKind.FromKeyword OrElse contextualKeyword.Kind = SyntaxKind.AggregateKeyword) Debug.Assert(CurrentToken.Text = contextualKeyword.Text) ' // Look ahead and see if it looks like a query, i.e. ' // {AGGREGATE | FROM } <id>[?] {In | As | = } Dim newLineAfterFrom As Boolean = False Dim curIndex = 1 Dim current As SyntaxToken = PeekToken(curIndex) If current IsNot Nothing AndAlso current.IsEndOfLine() Then If Not NextLineStartsWithStatementTerminator(1) Then '// we don't allow two EOLs in a row here curIndex += 1 current = PeekToken(curIndex) newLineAfterFrom = True End If End If ' <id> If current Is Nothing OrElse (current.Kind <> SyntaxKind.IdentifierToken AndAlso Not current.IsKeyword) Then Return Nothing End If ' "FROM <id> <EOL>" on the same line is enough to consider this a query. Only do the full check if ' there is a new line between the FROM and the <id>, if this is a keyword after the FROM or if the identifier is ' a contextual keyword. ' ' These are query expressions: ' x = From y (it's important for the IDE to have this classified as a query to offer correct highlighting ' and completion suggestions) ' or ' x = from y ' In ... ' This logic is needed to not classify the identifier "From" in the following JoinCondition as query: ' dim f = Join x On From Equals y ' ' These are two assignments: ' x = From ' y = ' and ' x = from a With ' ' See Bugs 1678 and 10020 for more context. ' Dim identifierAsContextualKeyword As KeywordSyntax = Nothing If newLineAfterFrom OrElse current.IsKeyword OrElse (current.Kind = SyntaxKind.IdentifierToken AndAlso TryTokenAsContextualKeyword(current, identifierAsContextualKeyword)) Then ' Note: this block is used to reject queries. Everything that skips this if ' will be classified as a query. curIndex += 1 current = PeekToken(curIndex) ' // Look ahead for 'IN' as it can start it's own line If current IsNot Nothing Then If current.Kind = SyntaxKind.StatementTerminatorToken Then current = PeekToken(curIndex + 1) If current Is Nothing OrElse current.Kind <> SyntaxKind.InKeyword Then Return Nothing End If ElseIf current.Kind = SyntaxKind.QuestionToken Then ' // Skip '?' curIndex += 1 current = PeekToken(curIndex) End If End If If current Is Nothing Then Return Nothing End If ' check for As, In or = ' note that = may not come after EoL If current.Kind <> SyntaxKind.InKeyword AndAlso current.Kind <> SyntaxKind.AsKeyword AndAlso (newLineAfterFrom OrElse current.Kind <> SyntaxKind.EqualsToken) Then Return Nothing End If End If If contextualKeyword.Kind = SyntaxKind.FromKeyword Then GetNextToken() 'get off the From Return ParseFromQueryExpression(contextualKeyword) Else Debug.Assert(contextualKeyword.Kind = SyntaxKind.AggregateKeyword) GetNextToken() 'get off the Aggregate Return ParseAggregateQueryExpression(contextualKeyword) End If End Function Private Function ParseGroupByExpression(groupKw As KeywordSyntax) As GroupByClauseSyntax Debug.Assert(groupKw IsNot Nothing) Dim byKw As KeywordSyntax = Nothing Dim elements As CodeAnalysis.Syntax.InternalSyntax.SeparatedSyntaxList(Of ExpressionRangeVariableSyntax) = Nothing If Not TryEatNewLineAndGetContextualKeyword(SyntaxKind.ByKeyword, byKw, createIfMissing:=False) Then TryEatNewLine() ' // parse element selector elements = ParseSelectList() End If Dim keys As CodeAnalysis.Syntax.InternalSyntax.SeparatedSyntaxList(Of ExpressionRangeVariableSyntax) = Nothing If byKw IsNot Nothing OrElse TryEatNewLineAndGetContextualKeyword(SyntaxKind.ByKeyword, byKw, createIfMissing:=True) Then TryEatNewLine() ' // parse key selector keys = ParseSelectList() Else Dim rangeVariables = Me._pool.AllocateSeparated(Of ExpressionRangeVariableSyntax)() rangeVariables.Add(InternalSyntaxFactory.ExpressionRangeVariable(Nothing, InternalSyntaxFactory.MissingExpression())) keys = rangeVariables.ToList Me._pool.Free(rangeVariables) End If Dim intoKw As KeywordSyntax = Nothing Dim Aggregation As CodeAnalysis.Syntax.InternalSyntax.SeparatedSyntaxList(Of AggregationRangeVariableSyntax) = Nothing If TryEatNewLineAndGetContextualKeyword(SyntaxKind.IntoKeyword, intoKw, createIfMissing:=True) Then TryEatNewLine() ' // parse result selector Aggregation = ParseAggregateList(True, False) Else Aggregation = Me.MissingAggregationRangeVariables() End If Return SyntaxFactory.GroupByClause(groupKw, elements, byKw, keys, intoKw, Aggregation) End Function Private Function MissingAggregationRangeVariables() As CodeAnalysis.Syntax.InternalSyntax.SeparatedSyntaxList(Of AggregationRangeVariableSyntax) Dim rangeVariables = Me._pool.AllocateSeparated(Of AggregationRangeVariableSyntax)() rangeVariables.Add(InternalSyntaxFactory.AggregationRangeVariable(Nothing, SyntaxFactory.FunctionAggregation(InternalSyntaxFactory.MissingIdentifier(), Nothing, Nothing, Nothing))) Dim result As CodeAnalysis.Syntax.InternalSyntax.SeparatedSyntaxList(Of AggregationRangeVariableSyntax) = rangeVariables.ToList Me._pool.Free(rangeVariables) Return result End Function ' File: Parser.cpp ' Lines: 15016 - 15016 ' GroupJoinExpression* .Parser::ParseGroupJoinExpression( [ _In_ Token* beginSourceToken ] [ _In_opt_ ParseTree::LinqExpression* Source ] [ _Inout_ bool& ErrorInConstruct ] ) Private Function ParseInnerJoinOrGroupJoinExpression(groupKw As KeywordSyntax, joinKw As KeywordSyntax) As JoinClauseSyntax Debug.Assert(joinKw IsNot Nothing) ' // Make sure control var on the left is always named ' TODO: semantics ' MakeSureLeftControlVarIsNamed(source) TryEatNewLine() Dim joinVariable = ParseJoinControlVar() Dim moreJoinsBuilder = _pool.Allocate(Of JoinClauseSyntax)() Do Dim nextJoin = ParseOptionalJoinOperator() If nextJoin Is Nothing Then Exit Do End If moreJoinsBuilder.Add(nextJoin) Loop Dim onKw As KeywordSyntax = Nothing Dim Predicate As CodeAnalysis.Syntax.InternalSyntax.SeparatedSyntaxList(Of JoinConditionSyntax) = Nothing If TryEatNewLineAndGetToken(SyntaxKind.OnKeyword, onKw, createIfMissing:=True) Then TryEatNewLine() Predicate = ParseJoinPredicateExpression() Else Dim missingEq = SyntaxFactory.JoinCondition(InternalSyntaxFactory.MissingExpression, InternalSyntaxFactory.MissingKeyword(SyntaxKind.EqualsKeyword), InternalSyntaxFactory.MissingExpression) Predicate = New CodeAnalysis.Syntax.InternalSyntax.SeparatedSyntaxList(Of JoinConditionSyntax)(missingEq) End If Dim joinVarList = New CodeAnalysis.Syntax.InternalSyntax.SeparatedSyntaxList(Of CollectionRangeVariableSyntax)(joinVariable) Dim moreJoins = moreJoinsBuilder.ToList() _pool.Free(moreJoinsBuilder) If groupKw Is Nothing Then Return SyntaxFactory.SimpleJoinClause(joinKw, joinVarList, moreJoins, onKw, Predicate) Else Dim intoKw As KeywordSyntax = Nothing Dim Aggregation As CodeAnalysis.Syntax.InternalSyntax.SeparatedSyntaxList(Of AggregationRangeVariableSyntax) = Nothing If TryEatNewLineAndGetContextualKeyword(SyntaxKind.IntoKeyword, intoKw, createIfMissing:=True) Then TryEatNewLine() ' // parse result selector Aggregation = ParseAggregateList(True, True) Else Aggregation = Me.MissingAggregationRangeVariables() End If Return SyntaxFactory.GroupJoinClause(groupKw, joinKw, joinVarList, moreJoins, onKw, Predicate, intoKw, Aggregation) End If End Function ' File: Parser.cpp ' Lines: 15260 - 15260 ' LinqExpression* .Parser::ParseJoinSourceExpression( [ _Inout_ bool& ErrorInConstruct ] ) Private Function ParseOptionalJoinOperator() As JoinClauseSyntax Dim joinKw As KeywordSyntax = Nothing If TryEatNewLineAndGetContextualKeyword(SyntaxKind.JoinKeyword, joinKw) Then Return ParseInnerJoinOrGroupJoinExpression(Nothing, joinKw) End If Dim groupKw As KeywordSyntax = Nothing If TryEatNewLineAndGetContextualKeyword(SyntaxKind.GroupKeyword, groupKw) Then TryGetContextualKeyword(SyntaxKind.JoinKeyword, joinKw, createIfMissing:=True) Return ParseInnerJoinOrGroupJoinExpression(groupKw, joinKw) End If Return Nothing End Function ' File: Parser.cpp ' Lines: 15107 - 15107 ' LinqSourceExpression* .Parser::ParseJoinControlVarExpression( [ _Inout_ bool& ErrorInConstruct ] ) Private Function ParseJoinControlVar() As CollectionRangeVariableSyntax Dim varName As ModifiedIdentifierSyntax = ParseNullableModifiedIdentifier() If varName.ContainsDiagnostics Then ' // If we see As or In before other query operators, then assume that ' // we are still on the Control Variable Declaration. ' // Otherwise, don't resync and allow the caller to ' // decide how to recover. Dim peek = PeekAheadFor(SyntaxKind.AsKeyword, SyntaxKind.InKeyword, SyntaxKind.OnKeyword, SyntaxKind.FromKeyword, SyntaxKind.WhereKeyword, SyntaxKind.GroupKeyword, SyntaxKind.SelectKeyword, SyntaxKind.OrderKeyword, SyntaxKind.JoinKeyword, SyntaxKind.DistinctKeyword, SyntaxKind.AggregateKeyword, SyntaxKind.IntoKeyword, SyntaxKind.SkipKeyword, SyntaxKind.TakeKeyword, SyntaxKind.LetKeyword) Select Case (peek) Case SyntaxKind.AsKeyword, SyntaxKind.InKeyword, SyntaxKind.GroupKeyword, SyntaxKind.JoinKeyword, SyntaxKind.OnKeyword varName = varName.AddTrailingSyntax(ResyncAt({peek})) End Select End If If CurrentToken.Kind = SyntaxKind.QuestionToken AndAlso PeekToken(1).Kind = SyntaxKind.InKeyword Then Dim unexpectedNullable As SyntaxToken = CurrentToken varName = varName.AddTrailingSyntax(ReportSyntaxError(unexpectedNullable, ERRID.ERR_NullableTypeInferenceNotSupported)) GetNextToken() ' get off the "?" End If Dim AsClause As SimpleAsClauseSyntax = Nothing ' // Parse the type if specified If CurrentToken.Kind = SyntaxKind.AsKeyword Then Dim AsKw As KeywordSyntax = DirectCast(CurrentToken, KeywordSyntax) Debug.Assert(AsKw IsNot Nothing) ' // Parse the type GetNextToken() ' // get off AS Dim Type As TypeSyntax = ParseGeneralType() AsClause = SyntaxFactory.SimpleAsClause(AsKw, Nothing, Type) ' // try to recover If Type.ContainsDiagnostics Then Dim peek = PeekAheadFor(SyntaxKind.InKeyword, SyntaxKind.OnKeyword, SyntaxKind.EqualsToken, SyntaxKind.FromKeyword, SyntaxKind.WhereKeyword, SyntaxKind.GroupKeyword, SyntaxKind.SelectKeyword, SyntaxKind.OrderKeyword, SyntaxKind.JoinKeyword, SyntaxKind.DistinctKeyword, SyntaxKind.AggregateKeyword, SyntaxKind.IntoKeyword, SyntaxKind.SkipKeyword, SyntaxKind.TakeKeyword, SyntaxKind.LetKeyword) Select Case (peek) Case SyntaxKind.EqualsToken, SyntaxKind.InKeyword, SyntaxKind.GroupKeyword, SyntaxKind.JoinKeyword, SyntaxKind.OnKeyword AsClause = AsClause.AddTrailingSyntax(ResyncAt({peek})) End Select End If End If Dim [In] As KeywordSyntax = Nothing Dim source As ExpressionSyntax = Nothing If TryEatNewLineAndGetToken(SyntaxKind.InKeyword, [In], createIfMissing:=True) Then TryEatNewLine() ' // dev10_500708 allow line continuation after 'IN' source = ParseExpressionCore() End If ' // try to recover If source Is Nothing OrElse source.ContainsDiagnostics Then ' Fix up source source = If(source, InternalSyntaxFactory.MissingExpression) ' resync Dim peek = PeekAheadFor(SyntaxKind.CommaToken, SyntaxKind.FromKeyword, SyntaxKind.WhereKeyword, SyntaxKind.GroupKeyword, SyntaxKind.SelectKeyword, SyntaxKind.OrderKeyword, SyntaxKind.JoinKeyword, SyntaxKind.DistinctKeyword, SyntaxKind.AggregateKeyword, SyntaxKind.IntoKeyword, SyntaxKind.SkipKeyword, SyntaxKind.TakeKeyword, SyntaxKind.LetKeyword) If peek = SyntaxKind.CommaToken Then source = source.AddTrailingSyntax(ResyncAt({peek})) End If End If Return SyntaxFactory.CollectionRangeVariable(varName, AsClause, [In], source) End Function ' File: Parser.cpp ' Lines: 15293 - 15293 ' Expression* .Parser::ParseJoinPredicateExpression( [ _Inout_ bool& ErrorInConstruct ] ) Private Function ParseJoinPredicateExpression() As CodeAnalysis.Syntax.InternalSyntax.SeparatedSyntaxList(Of JoinConditionSyntax) Dim Exprs = Me._pool.AllocateSeparated(Of JoinConditionSyntax)() Dim AndTk As KeywordSyntax = Nothing Do Dim element As JoinConditionSyntax = Nothing If CurrentToken.Kind <> SyntaxKind.StatementTerminatorToken Then Dim Left = ParseExpressionCore(OperatorPrecedence.PrecedenceRelational) ' // try to recover If Left.ContainsDiagnostics Then Left = ResyncAt(Left, SyntaxKind.EqualsToken, SyntaxKind.FromKeyword, SyntaxKind.WhereKeyword, SyntaxKind.GroupKeyword, SyntaxKind.SelectKeyword, SyntaxKind.OrderKeyword, SyntaxKind.JoinKeyword, SyntaxKind.DistinctKeyword, SyntaxKind.AggregateKeyword, SyntaxKind.IntoKeyword, SyntaxKind.OnKeyword, SyntaxKind.AndKeyword, SyntaxKind.AndAlsoKeyword, SyntaxKind.OrKeyword, SyntaxKind.OrElseKeyword, SyntaxKind.SkipKeyword, SyntaxKind.SkipKeyword, SyntaxKind.LetKeyword) End If Dim eqKw As KeywordSyntax = Nothing Dim Right As ExpressionSyntax = Nothing If TryGetContextualKeywordAndEatNewLine(SyntaxKind.EqualsKeyword, eqKw, createIfMissing:=True) Then Right = ParseExpressionCore(OperatorPrecedence.PrecedenceRelational) Else Right = InternalSyntaxFactory.MissingExpression End If element = SyntaxFactory.JoinCondition(Left, eqKw, Right) Else element = SyntaxFactory.JoinCondition(InternalSyntaxFactory.MissingExpression, InternalSyntaxFactory.MissingKeyword(SyntaxKind.EqualsKeyword), InternalSyntaxFactory.MissingExpression) element = ReportSyntaxError(element, ERRID.ERR_ExpectedExpression) End If ' // try to recover If element.ContainsDiagnostics Then element = ResyncAt(element, SyntaxKind.AndKeyword, SyntaxKind.FromKeyword, SyntaxKind.WhereKeyword, SyntaxKind.GroupKeyword, SyntaxKind.SelectKeyword, SyntaxKind.OrderKeyword, SyntaxKind.JoinKeyword, SyntaxKind.DistinctKeyword, SyntaxKind.AggregateKeyword, SyntaxKind.IntoKeyword, SyntaxKind.OnKeyword, SyntaxKind.AndAlsoKeyword, SyntaxKind.OrKeyword, SyntaxKind.OrElseKeyword, SyntaxKind.SkipKeyword, SyntaxKind.TakeKeyword, SyntaxKind.LetKeyword) End If If Exprs.Count > 0 Then Exprs.AddSeparator(AndTk) End If Exprs.Add(element) If TryGetTokenAndEatNewLine(SyntaxKind.AndKeyword, AndTk) Then Continue Do ElseIf CurrentToken.Kind = SyntaxKind.AndAlsoKeyword OrElse CurrentToken.Kind = SyntaxKind.OrKeyword OrElse CurrentToken.Kind = SyntaxKind.OrElseKeyword Then Exprs(Exprs.Count - 1) = Exprs(Exprs.Count - 1).AddTrailingSyntax(CurrentToken, ERRID.ERR_ExpectedAnd) GetNextToken() ' consume bad token End If Exit Do Loop Dim result = Exprs.ToList Me._pool.Free(Exprs) ' // try to recover If result.Node.ContainsDiagnostics Then Dim elements = result.Node elements = ResyncAt(elements, SyntaxKind.FromKeyword, SyntaxKind.WhereKeyword, SyntaxKind.GroupKeyword, SyntaxKind.SelectKeyword, SyntaxKind.OrderKeyword, SyntaxKind.JoinKeyword, SyntaxKind.DistinctKeyword, SyntaxKind.AggregateKeyword, SyntaxKind.IntoKeyword, SyntaxKind.OnKeyword, SyntaxKind.SkipKeyword, SyntaxKind.TakeKeyword, SyntaxKind.LetKeyword) result = New CodeAnalysis.Syntax.InternalSyntax.SeparatedSyntaxList(Of JoinConditionSyntax)(CType(New CodeAnalysis.Syntax.InternalSyntax.SyntaxList(Of JoinConditionSyntax)(CType(elements, GreenNode)), CodeAnalysis.Syntax.InternalSyntax.SyntaxList(Of GreenNode))) End If Return result End Function ' File: Parser.cpp ' Lines: 14861 - 14861 ' LinqExpression* .Parser::ParseFromExpression( [ bool StartingQuery ] [ bool ImplicitFrom ] [ _Inout_ bool& ErrorInConstruct ] ) Private Function ParseFromOperator(FromKw As KeywordSyntax) As FromClauseSyntax Debug.Assert(FromKw IsNot Nothing) TryEatNewLine() Return SyntaxFactory.FromClause(FromKw, ParseFromControlVars()) End Function Private Function ParseLetOperator(LetKw As KeywordSyntax) As LetClauseSyntax Debug.Assert(LetKw IsNot Nothing) TryEatNewLine() Return SyntaxFactory.LetClause(LetKw, ParseLetList()) End Function ' File: Parser.cpp ' Lines: 15389 - 15389 'ParseTree::OrderByList * 'Parser::ParseOrderByList '( ' _Inout_ bool &ErrorInConstruct ' _Inout_ ParseTree::LineContinuationState &OrderExprContinuationState, // [out] the continuation situation for the Order statement as relates to the Ascending/Descending keywords ' _Inout_ bool &ErrorInConstruct // [out] whether we ran into an error processing the OrderBy list. ') Private Function ParseOrderByList() As CodeAnalysis.Syntax.InternalSyntax.SeparatedSyntaxList(Of OrderingSyntax) Dim exprs = Me._pool.AllocateSeparated(Of OrderingSyntax)() Do Dim OrderExpression = ParseExpressionCore() ' // try to recover If OrderExpression.ContainsDiagnostics Then OrderExpression = ResyncAt(OrderExpression, SyntaxKind.CommaToken, SyntaxKind.AscendingKeyword, SyntaxKind.DescendingKeyword, SyntaxKind.WhereKeyword, SyntaxKind.GroupKeyword, SyntaxKind.SelectKeyword, SyntaxKind.OrderKeyword, SyntaxKind.JoinKeyword, SyntaxKind.FromKeyword, SyntaxKind.DistinctKeyword, SyntaxKind.AggregateKeyword, SyntaxKind.IntoKeyword, SyntaxKind.SkipKeyword, SyntaxKind.TakeKeyword, SyntaxKind.LetKeyword) End If Dim element As OrderingSyntax = Nothing Dim directionKw As KeywordSyntax = Nothing If TryEatNewLineAndGetContextualKeyword(SyntaxKind.DescendingKeyword, directionKw) Then element = SyntaxFactory.DescendingOrdering(OrderExpression, directionKw) Else TryEatNewLineAndGetContextualKeyword(SyntaxKind.AscendingKeyword, directionKw) element = SyntaxFactory.AscendingOrdering(OrderExpression, directionKw) End If exprs.Add(element) Dim comma As PunctuationSyntax = Nothing If TryEatNewLineAndGetToken(SyntaxKind.CommaToken, comma, createIfMissing:=False) Then TryEatNewLine() exprs.AddSeparator(comma) Else Exit Do End If Loop Dim result = exprs.ToList Me._pool.Free(exprs) Return result End Function ' File: Parser.cpp ' Lines: 15508 - 15508 ' LinqExpression* .Parser::ParseFromQueryExpression( [ bool ImplicitFrom ] [ _Inout_ bool& ErrorInConstruct ] ) Private Sub ParseMoreQueryOperators(ByRef operators As SyntaxListBuilder(Of QueryClauseSyntax)) Do ' // try to recover If operators.Count > 0 AndAlso operators(operators.Count - 1).ContainsDiagnostics Then operators(operators.Count - 1) = ResyncAt(operators(operators.Count - 1), SyntaxKind.FromKeyword, SyntaxKind.WhereKeyword, SyntaxKind.GroupKeyword, SyntaxKind.SelectKeyword, SyntaxKind.OrderKeyword, SyntaxKind.JoinKeyword, SyntaxKind.DistinctKeyword, SyntaxKind.AggregateKeyword, SyntaxKind.IntoKeyword, SyntaxKind.SkipKeyword, SyntaxKind.TakeKeyword, SyntaxKind.LetKeyword) End If Dim clause = ParseNextQueryOperator() If clause Is Nothing Then Return End If operators.Add(clause) Loop End Sub Private Function ParseNextQueryOperator() As QueryClauseSyntax Dim Start = CurrentToken ' //We allow implicit line continuations before query keywords when in a query context. If Start.Kind = SyntaxKind.StatementTerminatorToken Then If NextLineStartsWithStatementTerminator() OrElse Not IsContinuableQueryOperator(PeekToken(1)) Then Return Nothing End If ' we are going to use this EoL and next token since we know it is a valid operator. ' so it is ok to move to the next token and grab EoL as a trivia. TryEatNewLine() Start = CurrentToken End If ' // it must be Id or Keyword from here Select Case Start.Kind Case SyntaxKind.SelectKeyword GetNextToken() ' get off Select TryEatNewLine() Dim Projection = ParseSelectList() Return InternalSyntaxFactory.SelectClause(DirectCast(Start, KeywordSyntax), Projection) Case SyntaxKind.LetKeyword GetNextToken() ' get off Let Return ParseLetOperator(DirectCast(Start, KeywordSyntax)) Case SyntaxKind.IdentifierToken Dim kw As KeywordSyntax = Nothing If Not TryTokenAsContextualKeyword(Start, kw) Then Return Nothing End If Select Case kw.Kind Case SyntaxKind.WhereKeyword GetNextToken() ' get off where TryEatNewLine() Return InternalSyntaxFactory.WhereClause(kw, ParseExpressionCore()) Case SyntaxKind.SkipKeyword GetNextToken() ' get off Skip If CurrentToken.Kind = SyntaxKind.WhileKeyword Then ' // Note: no line continuation if Skip is followed by While, i.e. Skip While is treated as a unit Dim whileKw = DirectCast(CurrentToken, KeywordSyntax) GetNextToken() ' get off While TryEatNewLine() Return InternalSyntaxFactory.SkipWhileClause(kw, whileKw, ParseExpressionCore()) Else TryEatNewLineIfNotFollowedBy(SyntaxKind.WhileKeyword) ' // when Skip ends the line, allow a implicit line continuation Return InternalSyntaxFactory.SkipClause(kw, ParseExpressionCore()) End If Case SyntaxKind.TakeKeyword GetNextToken() ' get off Take If CurrentToken.Kind = SyntaxKind.WhileKeyword Then ' // Note: no line continuation if Skip is followed by While, i.e. Skip While is treated as a unit Dim whileKw = DirectCast(CurrentToken, KeywordSyntax) GetNextToken() ' get off While TryEatNewLine() Return InternalSyntaxFactory.TakeWhileClause(kw, whileKw, ParseExpressionCore()) Else TryEatNewLineIfNotFollowedBy(SyntaxKind.WhileKeyword) ' // when Skip ends the line, allow a implicit line continuation Return InternalSyntaxFactory.TakeClause(kw, ParseExpressionCore()) End If Case SyntaxKind.GroupKeyword GetNextToken() 'get off Group ' // See if this is a 'Group Join' Dim joinKw As KeywordSyntax = Nothing If TryGetContextualKeyword(SyntaxKind.JoinKeyword, joinKw) Then Return ParseInnerJoinOrGroupJoinExpression(kw, joinKw) Else Return ParseGroupByExpression(kw) End If Case SyntaxKind.AggregateKeyword GetNextToken() ' get off Aggregate Return ParseAggregateClause(kw) Case SyntaxKind.OrderKeyword GetNextToken() ' get off Order Dim byKw As KeywordSyntax = Nothing TryGetContextualKeywordAndEatNewLine(SyntaxKind.ByKeyword, byKw, createIfMissing:=True) TryEatNewLine() Dim OrderByItems = ParseOrderByList() Return InternalSyntaxFactory.OrderByClause(kw, byKw, OrderByItems) Case SyntaxKind.DistinctKeyword GetNextToken() ' get off Distinct If CurrentToken.Kind = SyntaxKind.StatementTerminatorToken Then ' Eat the new line only if the distinct is followed by a token that can continue a term or an expression Dim tokenAfterEOL = PeekToken(1) Select Case tokenAfterEOL.Kind Case SyntaxKind.DotToken, SyntaxKind.ExclamationToken, SyntaxKind.QuestionToken, SyntaxKind.OpenParenToken TryEatNewLine() Case Else If tokenAfterEOL.IsBinaryOperator Then TryEatNewLine() End If End Select End If Return InternalSyntaxFactory.DistinctClause(kw) Case SyntaxKind.JoinKeyword GetNextToken() ' get off Join Return ParseInnerJoinOrGroupJoinExpression(Nothing, kw) Case SyntaxKind.FromKeyword GetNextToken() ' get off From Return ParseFromOperator(kw) End Select End Select Return Nothing End Function Private Function ParseFromQueryExpression(fromKw As KeywordSyntax) As QueryExpressionSyntax Debug.Assert(fromKw IsNot Nothing) Dim operators = Me._pool.Allocate(Of QueryClauseSyntax)() operators.Add(ParseFromOperator(fromKw)) ParseMoreQueryOperators(operators) Dim result = operators.ToList Me._pool.Free(operators) Return SyntaxFactory.QueryExpression(result) End Function Private Function ParseAggregateQueryExpression(AggregateKw As KeywordSyntax) As QueryExpressionSyntax Debug.Assert(AggregateKw IsNot Nothing) Dim operators = Me._pool.Allocate(Of QueryClauseSyntax)() operators.Add(ParseAggregateClause(AggregateKw)) Dim result = operators.ToList Me._pool.Free(operators) Return SyntaxFactory.QueryExpression(result) End Function Private Function ParseAggregateClause(AggregateKw As KeywordSyntax) As AggregateClauseSyntax Debug.Assert(AggregateKw IsNot Nothing) TryEatNewLine() Dim controlVariables = ParseFromControlVars() Dim moreOperators = Me._pool.Allocate(Of QueryClauseSyntax)() ParseMoreQueryOperators(moreOperators) Dim operatorList = moreOperators.ToList Me._pool.Free(moreOperators) Dim intoKw As KeywordSyntax = Nothing Dim variables As CodeAnalysis.Syntax.InternalSyntax.SeparatedSyntaxList(Of AggregationRangeVariableSyntax) = Nothing If TryEatNewLineAndGetContextualKeyword(SyntaxKind.IntoKeyword, intoKw, createIfMissing:=True) Then ' //ILC: I took the liberty of adding implicit line continuations after query keywords in addition to before them... TryEatNewLine() ' // parse result selector variables = ParseAggregateList(False, False) Else variables = Me.MissingAggregationRangeVariables() End If Return SyntaxFactory.AggregateClause(AggregateKw, controlVariables, operatorList, intoKw, variables) End Function ' bool .Parser::IsContinuableQueryOperator( [ Token* pToken ] ) Private Function IsContinuableQueryOperator(pToken As SyntaxToken) As Boolean Debug.Assert(pToken IsNot Nothing) Debug.Assert(pToken.Text Is PeekToken(1).Text) Dim kind As SyntaxKind = Nothing If Not TryTokenAsKeyword(pToken, kind) Then Return False End If Dim isQueryKwd As Boolean = KeywordTable.IsQueryClause(kind) If isQueryKwd AndAlso kind = SyntaxKind.SelectKeyword Then ' //We do not want to allow an implicit line continuation before a "select" keyword if it is immediately ' //followed by the "case" keyword. This allows code like the following to parse correctly: ' // dim a = from x in xs ' // select case b ' // end select Dim nextToken = PeekToken(2) If nextToken.Kind = SyntaxKind.CaseKeyword Then isQueryKwd = False End If End If Return isQueryKwd End Function ' File: Parser.cpp ' Lines: 14918 - 14918 ' .::MakeSureLeftControlVarIsNamed( [ _In_opt_ ParseTree::LinqExpression* Source ] ) ' TODO: this should be done in semantics. 'Private Shared Sub MakeSureLeftControlVarIsNamed( _ ' ByVal Source As ParseTree.LinqExpression _ ') ' While Source IsNot Nothing ' Select Case (Source.Opcode) ' Case ParseTree.Expression.Opcodes.InnerJoin, _ ' ParseTree.Expression.Opcodes.GroupJoin, _ ' ParseTree.Expression.Opcodes.CrossJoin, _ ' ParseTree.Expression.Opcodes.From, _ ' ParseTree.Expression.Opcodes.Let, _ ' ParseTree.Expression.Opcodes.GroupBy, _ ' ParseTree.Expression.Opcodes.Aggregate, _ ' ParseTree.Expression.Opcodes.LinqSource ' Return ' // these operators do not produce unnamed vars ' Case ParseTree.Expression.Opcodes.Where, _ ' ParseTree.Expression.Opcodes.SkipWhile, _ ' ParseTree.Expression.Opcodes.TakeWhile, _ ' ParseTree.Expression.Opcodes.Take, _ ' ParseTree.Expression.Opcodes.Skip, _ ' ParseTree.Expression.Opcodes.Distinct, _ ' ParseTree.Expression.Opcodes.OrderBy ' Source = Source.AsLinqOperator().Source ' // these operators do not declare control vars ' Case ParseTree.Expression.Opcodes.Select ' AssertIfTrue(Source.AsSelect().ForceNameInferenceForSingleElement) ' Source.AsSelect().ForceNameInferenceForSingleElement = True ' Return ' // done ' Case Else ' AssertIfFalse(False) ' // unknown Opcode ' Return ' End Select ' End While 'End Sub End Class End Namespace