SyntaxTreeExtensions.vb 54.5 KB
Newer Older
1
' Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.
P
Pilchie 已提交
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

Imports System.Runtime.CompilerServices
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Imports Microsoft.CodeAnalysis.VisualBasic.Utilities
Imports System.Threading

Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions.ContextQuery
    Friend Module SyntaxTreeExtensions

        <Extension()>
        Friend Function GetTargetToken(syntaxTree As SyntaxTree, position As Integer, cancellationToken As CancellationToken) As SyntaxToken
            Dim token = syntaxTree.FindTokenOnLeftOfPosition(
                position, cancellationToken,
                includeDirectives:=syntaxTree.IsInPreprocessorDirectiveContext(position, cancellationToken),
                includeDocumentationComments:=True)

18
            Do While token.Kind <> SyntaxKind.None
P
Pilchie 已提交
19 20 21 22 23 24
                ' If we have a non-word token to our left, we should always stop there
                If Not token.IsWord() AndAlso token.Span.End <= position Then
                    Exit Do
                End If

                ' If this token is to our left, return it
25
                If Not token.IsKind(SyntaxKind.EmptyToken) AndAlso token.Span.End < position Then
P
Pilchie 已提交
26 27 28 29 30 31 32 33 34
                    Exit Do
                End If

                token = token.GetPreviousToken()
            Loop

            Return token
        End Function

35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
        <Extension()>
        Public Function IsPreProcessorKeywordContext(syntaxTree As SyntaxTree, position As Integer, cancellationToken As CancellationToken) As Boolean
            Return IsPreProcessorKeywordContext(
                syntaxTree, position,
                syntaxTree.FindTokenOnLeftOfPosition(position, cancellationToken, includeDirectives:=True),
                cancellationToken)
        End Function

        <Extension()>
        Public Function IsPreProcessorKeywordContext(syntaxTree As SyntaxTree, position As Integer, preProcessorTokenOnLeftOfPosition As SyntaxToken, cancellationToken As CancellationToken) As Boolean
            ' cases:
            '  #|
            '  #d|
            '  # |
            '  # d|

            ' note comments are Not allowed between the # And item.
            Dim token = preProcessorTokenOnLeftOfPosition
            token = token.GetPreviousTokenIfTouchingWord(position)

A
Artur Spychaj 已提交
55
            Return token.HasAncestor(Of DirectiveTriviaSyntax)
56 57
        End Function

P
Pilchie 已提交
58 59 60 61 62
        <Extension()>
        Public Function IsNamespaceContext(syntaxTree As SyntaxTree, position As Integer, token As SyntaxToken, cancellationToken As CancellationToken, Optional semanticModelOpt As SemanticModel = Nothing) As Boolean
            Return syntaxTree.IsTypeContext(position, token, cancellationToken, semanticModelOpt)
        End Function

63 64 65 66 67 68
        <Extension()>
        Public Function IsNamespaceDeclarationNameContext(syntaxTree As SyntaxTree, position As Integer, cancellationToken As CancellationToken) As Boolean
            Dim statement = syntaxTree.GetTargetToken(position, cancellationToken).GetAncestor(Of NamespaceStatementSyntax)
            Return statement IsNot Nothing AndAlso statement.Name.Span.IntersectsWith(position)
        End Function

P
Pilchie 已提交
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
        <Extension()>
        Public Function GetContainingTypeBlock(syntaxTree As SyntaxTree, position As Integer, cancellationToken As CancellationToken) As TypeBlockSyntax
            Dim token = syntaxTree.GetRoot(cancellationToken).FindToken(position)
            Return TryCast(token.GetInnermostDeclarationContext(), TypeBlockSyntax)
        End Function

        ''' <summary>
        ''' The specified position is where we can declare some .NET type, such as classes, structures, etc.
        ''' </summary>
        <Extension()>
        Friend Function IsTypeDeclarationContext(syntaxTree As SyntaxTree, position As Integer, token As SyntaxToken, cancellationToken As CancellationToken) As Boolean
            Return Not syntaxTree.IsInPreprocessorDirectiveContext(position, cancellationToken) AndAlso
                syntaxTree.IsDeclarationContextWithinTypeBlocks(position, token, True, cancellationToken, SyntaxKind.ClassBlock, SyntaxKind.StructureBlock, SyntaxKind.InterfaceBlock, SyntaxKind.NamespaceBlock, SyntaxKind.ModuleBlock, SyntaxKind.CompilationUnit)
        End Function

        <Extension()>
        Friend Function IsDeclarationContextWithinTypeBlocks(
            syntaxTree As SyntaxTree, position As Integer, targetToken As SyntaxToken, allowAfterModifiersOrDim As Boolean, cancellationToken As CancellationToken, ParamArray allowedParentBlocks As SyntaxKind()) As Boolean

            Contract.Requires(targetToken = syntaxTree.GetTargetToken(position, cancellationToken))
89
            If targetToken.Kind = SyntaxKind.None OrElse targetToken.Parent Is Nothing Then
P
Pilchie 已提交
90 91 92 93
                ' We're at the root, so we're acceptable if we allow us to be in the root
                Return allowedParentBlocks.Contains(SyntaxKind.CompilationUnit)
            End If

94 95 96 97
            If syntaxTree.IsInPreprocessorDirectiveContext(position, cancellationToken) Then
                Return False
            End If

P
Pilchie 已提交
98 99 100
            ' If we're within a method/event/property, then always no
            Dim method = targetToken.GetAncestor(Of MethodBlockBaseSyntax)()
            If method IsNot Nothing AndAlso
101 102 103
               (method.EndBlockStatement Is Nothing OrElse
                method.EndBlockStatement.IsMissing OrElse
                method.EndBlockStatement.BlockKeyword <> targetToken) Then
P
Pilchie 已提交
104 105 106 107 108 109 110 111 112 113 114 115 116 117

                Return False
            End If

            Dim [event] = targetToken.GetAncestor(Of EventBlockSyntax)()
            If [event] IsNot Nothing AndAlso
               ([event].EndEventStatement Is Nothing OrElse
                [event].EndEventStatement.IsMissing OrElse
                [event].EndEventStatement.BlockKeyword <> targetToken) Then

                Return False
            End If

            Dim afterDimOrModifiers = allowAfterModifiersOrDim AndAlso (targetToken.IsModifier OrElse
118
                                                                        targetToken.Kind = SyntaxKind.DimKeyword OrElse
P
Pilchie 已提交
119 120 121 122 123
                                                                        targetToken.HasMatchingText(SyntaxKind.AsyncKeyword) OrElse
                                                                        targetToken.HasMatchingText(SyntaxKind.IteratorKeyword))

            ' We either must be on a separate line, or else after Dim or modifiers
            If targetToken.FollowsEndOfStatement(position) OrElse afterDimOrModifiers Then
124
                Return targetToken.GetInnermostDeclarationContext().IsKind(allowedParentBlocks)
P
Pilchie 已提交
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
            End If

            Return False
        End Function

        ''' <summary>
        ''' The specified position is where a keyword can go like "Sub", "Function", etc. in a classes, structures, and modules
        ''' </summary>
        <Extension()>
        Friend Function IsTypeMemberDeclarationKeywordContext(syntaxTree As SyntaxTree, position As Integer, token As SyntaxToken, cancellationToken As CancellationToken) As Boolean
            Return syntaxTree.IsDeclarationContextWithinTypeBlocks(
                position, token, True, cancellationToken, SyntaxKind.ClassBlock, SyntaxKind.StructureBlock, SyntaxKind.ModuleBlock)
        End Function

        ''' <summary>
        ''' The specified position is where a keyword can go like "Sub", "Function" in an interface
        ''' </summary>
        <Extension()>
        Friend Function IsInterfaceMemberDeclarationKeywordContext(syntaxTree As SyntaxTree, position As Integer, token As SyntaxToken, cancellationToken As CancellationToken) As Boolean
            Return syntaxTree.IsDeclarationContextWithinTypeBlocks(position, token, True, cancellationToken, SyntaxKind.InterfaceBlock)
        End Function

        ''' <summary>
        ''' The specified position is where we can declare some .NET type, such as classes, structures, etc.
        ''' </summary>
        <Extension()>
        Friend Function IsTypeDeclarationKeywordContext(syntaxTree As SyntaxTree, position As Integer, token As SyntaxToken, cancellationToken As CancellationToken) As Boolean
            Return Not syntaxTree.IsInPreprocessorDirectiveContext(position, cancellationToken) AndAlso
                syntaxTree.IsDeclarationContextWithinTypeBlocks(
                    position, token, True, cancellationToken, SyntaxKind.ClassBlock, SyntaxKind.StructureBlock, SyntaxKind.InterfaceBlock, SyntaxKind.NamespaceBlock, SyntaxKind.ModuleBlock, SyntaxKind.CompilationUnit)
        End Function

        <Extension>
        Friend Function IsFieldNameDeclarationContext(syntaxTree As SyntaxTree, position As Integer, targetToken As SyntaxToken, cancellationToken As CancellationToken) As Boolean
            Contract.Requires(targetToken = syntaxTree.GetTargetToken(position, cancellationToken))
            If targetToken.FollowsEndOfStatement(position) Then
                Return False
            End If

164
            If targetToken.IsKind(SyntaxKind.ConstKeyword,
P
Pilchie 已提交
165 166 167 168
                                 SyntaxKind.DimKeyword,
                                 SyntaxKind.FriendKeyword,
                                 SyntaxKind.PrivateKeyword,
                                 SyntaxKind.ProtectedKeyword,
169
                                 SyntaxKind.ReadOnlyKeyword,
P
Pilchie 已提交
170 171 172 173 174 175 176 177
                                 SyntaxKind.PublicKeyword,
                                 SyntaxKind.ShadowsKeyword,
                                 SyntaxKind.SharedKeyword,
                                 SyntaxKind.WithEventsKeyword) Then

                Dim typeBlock = targetToken.GetAncestor(Of TypeBlockSyntax)()

                If typeBlock IsNot Nothing AndAlso
178
                       typeBlock.IsKind(SyntaxKind.ClassBlock,
P
Pilchie 已提交
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
                                             SyntaxKind.ModuleBlock,
                                             SyntaxKind.StructureBlock) Then

                    Dim modifierFacts = New ModifierCollectionFacts(syntaxTree, position, targetToken, cancellationToken)
                    Return modifierFacts.CouldApplyToOneOf(PossibleDeclarationTypes.Field)
                End If
            End If

            Return False
        End Function

        <Extension()>
        Friend Function IsParameterNameDeclarationContext(syntaxTree As SyntaxTree, position As Integer, cancellationToken As CancellationToken) As Boolean
            Dim targetToken = syntaxTree.GetTargetToken(position, cancellationToken)

194 195
            If targetToken.Parent.IsKind(SyntaxKind.ParameterList) AndAlso
                targetToken.IsKind(SyntaxKind.OpenParenToken,
P
Pilchie 已提交
196 197 198 199 200
                                  SyntaxKind.CommaToken) Then

                Return True
            End If

201 202
            If targetToken.Parent.IsKind(SyntaxKind.Parameter) AndAlso
                targetToken.IsKind(SyntaxKind.ByValKeyword,
P
Pilchie 已提交
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
                                  SyntaxKind.ByRefKeyword,
                                  SyntaxKind.ParamArrayKeyword,
                                  SyntaxKind.OptionalKeyword) Then

                Return True
            End If

            Return False
        End Function

        <Extension()>
        Friend Function IsLabelContext(syntaxTree As SyntaxTree, position As Integer, targetToken As SyntaxToken, cancellationToken As CancellationToken) As Boolean
            Contract.Requires(targetToken = syntaxTree.GetTargetToken(position, cancellationToken))
            If targetToken.FollowsEndOfStatement(position) Then
                Return False
            End If

            Dim gotoStatement = targetToken.GetAncestor(Of GoToStatementSyntax)()
            If gotoStatement IsNot Nothing Then
                If gotoStatement.GoToKeyword = targetToken Then
                    Return True
                End If

                If gotoStatement.Label.LabelToken = targetToken AndAlso targetToken.IntersectsWith(position) Then
                    Return True
                End If
            End If

            Dim onErrorGotoStatement = targetToken.GetAncestor(Of OnErrorGoToStatementSyntax)()
            If onErrorGotoStatement IsNot Nothing Then
                If onErrorGotoStatement.GoToKeyword = targetToken Then
                    Return True
                End If

                If onErrorGotoStatement.Label.LabelToken = targetToken AndAlso targetToken.IntersectsWith(position) Then
                    Return True
                End If
            End If

            Return False
        End Function

        <Extension()>
        Friend Function IsEnumMemberNameContext(syntaxTree As SyntaxTree, context As VisualBasicSyntaxContext) As Boolean
            Dim token = context.TargetToken

            ' Check to see if we're inside an enum block
            Dim enumBlock = token.GetAncestor(Of EnumBlockSyntax)()
            If enumBlock IsNot Nothing Then
                Return context.FollowsEndOfStatement OrElse token.IsChildToken(Of EnumMemberDeclarationSyntax)(Function(emds) emds.Identifier)
            End If

            Return False
        End Function

        <Extension()>
        Public Function IsDelegateCreationContext(syntaxTree As SyntaxTree, position As Integer, targetToken As SyntaxToken, semanticModel As SemanticModel, cancellationToken As CancellationToken) As Boolean
            Contract.Requires(targetToken = syntaxTree.GetTargetToken(position, cancellationToken))

            If targetToken.FollowsEndOfStatement(position) Then
                Return False
            End If

266
            If targetToken.Parent.IsKind(SyntaxKind.ArgumentList) AndAlso
P
Pilchie 已提交
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
               TypeOf targetToken.Parent.Parent Is NewExpressionSyntax Then

                Dim symbolInfo = semanticModel.GetSymbolInfo(DirectCast(targetToken.Parent.Parent, NewExpressionSyntax).Type())
                Dim objectCreationType = TryCast(symbolInfo.Symbol, ITypeSymbol)
                If objectCreationType IsNot Nothing AndAlso
                   objectCreationType.TypeKind = TypeKind.Delegate Then

                    Return True
                End If
            End If

            Return False
        End Function

        <Extension()>
        Friend Function IsExpressionContext(
            syntaxTree As SyntaxTree, position As Integer, cancellationToken As CancellationToken, Optional semanticModelOpt As SemanticModel = Nothing) As Boolean
            Dim targetToken = syntaxTree.GetTargetToken(position, cancellationToken)
            Return IsExpressionContext(syntaxTree, position, targetToken, cancellationToken, semanticModelOpt)
        End Function

        <Extension()>
        Friend Function IsExpressionContext(
            syntaxTree As SyntaxTree, position As Integer, targetToken As SyntaxToken, cancellationToken As CancellationToken, Optional semanticModelOpt As SemanticModel = Nothing) As Boolean

            Contract.Requires(targetToken = syntaxTree.GetTargetToken(position, cancellationToken))

294
            If targetToken.FollowsEndOfStatement(position) OrElse targetToken.Kind = SyntaxKind.None Then
P
Pilchie 已提交
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315
                Return False
            End If

            If semanticModelOpt IsNot Nothing Then
                If syntaxTree.IsDelegateCreationContext(position, targetToken, semanticModelOpt, cancellationToken) Then
                    Return False
                End If
            End If

            ' Easy ones first
            If targetToken.IsChildToken(Of AddRemoveHandlerStatementSyntax)(Function(handlerStatement) handlerStatement.AddHandlerOrRemoveHandlerKeyword) OrElse
               targetToken.IsChildToken(Of AddRemoveHandlerStatementSyntax)(Function(handlerStatement) handlerStatement.CommaToken) OrElse
               targetToken.IsChildToken(Of ArgumentListSyntax)(Function(argumentList) argumentList.OpenParenToken) OrElse
               targetToken.IsChildToken(Of AssignmentStatementSyntax)(Function(assignmentStatement) assignmentStatement.OperatorToken) OrElse
               targetToken.IsChildToken(Of AwaitExpressionSyntax)(Function(awaitExpression) awaitExpression.AwaitKeyword) OrElse
               targetToken.IsChildToken(Of BinaryExpressionSyntax)(Function(binaryExpression) binaryExpression.OperatorToken) OrElse
               targetToken.IsChildToken(Of BinaryConditionalExpressionSyntax)(Function(binaryExpression) binaryExpression.OpenParenToken) OrElse
               targetToken.IsChildToken(Of BinaryConditionalExpressionSyntax)(Function(binaryExpression) binaryExpression.CommaToken) OrElse
               targetToken.IsChildToken(Of CallStatementSyntax)(Function(callStatementSyntax) callStatementSyntax.CallKeyword) OrElse
               targetToken.IsChildToken(Of CatchFilterClauseSyntax)(Function(catchFilterClauseSyntax) catchFilterClauseSyntax.WhenKeyword) OrElse
               targetToken.IsChildToken(Of CaseStatementSyntax)(Function(caseStatement) caseStatement.CaseKeyword) OrElse
316
               targetToken.IsChildToken(Of ConditionalAccessExpressionSyntax)(Function(conditionalAccessExpressionSyntax) conditionalAccessExpressionSyntax.QuestionMarkToken) OrElse
P
Pilchie 已提交
317
               targetToken.IsChildSeparatorToken(Of CaseStatementSyntax, CaseClauseSyntax)(Function(caseStatement) caseStatement.Cases) OrElse
318 319
               targetToken.IsChildToken(Of RangeCaseClauseSyntax)(Function(rangeCaseClause) rangeCaseClause.ToKeyword) OrElse
               targetToken.IsChildToken(Of RelationalCaseClauseSyntax)(Function(relationalCaseClause) relationalCaseClause.OperatorToken) OrElse
P
Pilchie 已提交
320 321 322 323 324 325 326 327 328 329 330 331 332 333
               targetToken.IsChildToken(Of CastExpressionSyntax)(Function(castExpression) castExpression.OpenParenToken) OrElse
               targetToken.IsChildToken(Of CollectionInitializerSyntax)(Function(collectionInitializer) collectionInitializer.OpenBraceToken) OrElse
               targetToken.IsChildToken(Of CollectionRangeVariableSyntax)(Function(collectionRange) collectionRange.InKeyword) OrElse
               targetToken.IsChildToken(Of EraseStatementSyntax)(Function(eraseStatement) eraseStatement.EraseKeyword) OrElse
               targetToken.IsChildSeparatorToken(Of EraseStatementSyntax, ExpressionSyntax)(Function(eraseStatement) eraseStatement.Expressions) OrElse
               targetToken.IsChildToken(Of ErrorStatementSyntax)(Function(errorStatement) errorStatement.ErrorKeyword) OrElse
               targetToken.IsChildToken(Of ForStatementSyntax)(Function(forStatement) forStatement.EqualsToken) OrElse
               targetToken.IsChildToken(Of ForStatementSyntax)(Function(forStatement) forStatement.ToKeyword) OrElse
               targetToken.IsChildToken(Of ForStepClauseSyntax)(Function(forStepClause) forStepClause.StepKeyword) OrElse
               targetToken.IsChildToken(Of ForEachStatementSyntax)(Function(forEachStatement) forEachStatement.InKeyword) OrElse
               targetToken.IsChildToken(Of FunctionAggregationSyntax)(Function(functionAggregation) functionAggregation.OpenParenToken) OrElse
               targetToken.IsChildToken(Of GetTypeExpressionSyntax)(Function(getTypeExpression) getTypeExpression.OpenParenToken) OrElse
               targetToken.IsChildToken(Of GroupByClauseSyntax)(Function(groupBy) groupBy.GroupKeyword) OrElse
               targetToken.IsChildToken(Of GroupByClauseSyntax)(Function(groupBy) groupBy.ByKeyword) OrElse
334 335
               targetToken.IsChildToken(Of IfStatementSyntax)(Function(ifStatement) ifStatement.IfKeyword) OrElse
               targetToken.IsChildToken(Of ElseIfStatementSyntax)(Function(elseIfStatement) elseIfStatement.ElseIfKeyword) OrElse
P
Pilchie 已提交
336
               targetToken.IsChildToken(Of InferredFieldInitializerSyntax)(Function(inferredField) inferredField.KeyKeyword) OrElse
337
               targetToken.IsChildToken(Of InterpolationSyntax)(Function(interpolation) interpolation.OpenBraceToken) OrElse
P
Pilchie 已提交
338 339 340 341
               targetToken.IsChildToken(Of EqualsValueSyntax)(Function(initializer) initializer.EqualsToken) OrElse
               targetToken.IsChildToken(Of JoinClauseSyntax)(Function(joinQuery) joinQuery.OnKeyword) OrElse
               targetToken.IsChildSeparatorToken(Of JoinClauseSyntax, JoinConditionSyntax)(Function(joinQuery) joinQuery.JoinConditions) OrElse
               targetToken.IsChildToken(Of JoinConditionSyntax)(Function(joinCondition) joinCondition.EqualsKeyword) OrElse
342
               targetToken.IsChildToken(Of SimpleArgumentSyntax)(Function(argument) If(argument.IsNamed, argument.NameColonEquals.ColonEqualsToken, Nothing)) OrElse
P
Pilchie 已提交
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368
               targetToken.IsChildToken(Of NamedFieldInitializerSyntax)(Function(namedField) namedField.EqualsToken) OrElse
               targetToken.IsChildToken(Of OrderByClauseSyntax)(Function(orderByClause) orderByClause.ByKeyword) OrElse
               targetToken.IsChildSeparatorToken(Of OrderByClauseSyntax, OrderingSyntax)(Function(orderByClause) orderByClause.Orderings) OrElse
               targetToken.IsChildToken(Of ParenthesizedExpressionSyntax)(Function(parenthesizedExpression) parenthesizedExpression.OpenParenToken) OrElse
               targetToken.IsChildToken(Of PartitionClauseSyntax)(Function(partitionClause) partitionClause.SkipOrTakeKeyword) OrElse
               targetToken.IsChildToken(Of PartitionWhileClauseSyntax)(Function(partitionWhileClause) partitionWhileClause.WhileKeyword) OrElse
               targetToken.IsChildToken(Of PredefinedCastExpressionSyntax)(Function(buildInCast) buildInCast.OpenParenToken) OrElse
               targetToken.IsChildToken(Of RangeArgumentSyntax)(Function(rangeArgument) rangeArgument.ToKeyword) OrElse
               targetToken.IsChildToken(Of ReDimStatementSyntax)(Function(redimStatement) redimStatement.ReDimKeyword) OrElse
               targetToken.IsChildToken(Of ReDimStatementSyntax)(Function(redimStatement) redimStatement.PreserveKeyword) OrElse
               targetToken.IsChildToken(Of ReturnStatementSyntax)(Function(returnStatement) returnStatement.ReturnKeyword) OrElse
               targetToken.IsChildToken(Of SelectClauseSyntax)(Function(selectClause) selectClause.SelectKeyword) OrElse
               targetToken.IsChildSeparatorToken(Of SelectClauseSyntax, ExpressionRangeVariableSyntax)(Function(selectClause) selectClause.Variables) OrElse
               targetToken.IsChildToken(Of SelectStatementSyntax)(Function(selectStatement) selectStatement.SelectKeyword) OrElse
               targetToken.IsChildToken(Of SelectStatementSyntax)(Function(selectStatement) selectStatement.CaseKeyword) OrElse
               targetToken.IsChildToken(Of SyncLockStatementSyntax)(Function(syncLockStatement) syncLockStatement.SyncLockKeyword) OrElse
               targetToken.IsChildToken(Of TernaryConditionalExpressionSyntax)(Function(ternaryConditional) ternaryConditional.OpenParenToken) OrElse
               targetToken.IsChildToken(Of TernaryConditionalExpressionSyntax)(Function(ternaryConditional) ternaryConditional.FirstCommaToken) OrElse
               targetToken.IsChildToken(Of TernaryConditionalExpressionSyntax)(Function(ternaryConditional) ternaryConditional.SecondCommaToken) OrElse
               targetToken.IsChildToken(Of ThrowStatementSyntax)(Function(throwStatement) throwStatement.ThrowKeyword) OrElse
               targetToken.IsChildToken(Of TypeOfExpressionSyntax)(Function(typeOfIsExpression) typeOfIsExpression.TypeOfKeyword) OrElse
               targetToken.IsChildToken(Of UnaryExpressionSyntax)(Function(unaryExpression) unaryExpression.OperatorToken) OrElse
               targetToken.IsChildToken(Of UsingStatementSyntax)(Function(usingStatementSyntax) usingStatementSyntax.UsingKeyword) OrElse
               targetToken.IsChildToken(Of VariableNameEqualsSyntax)(Function(variableNameEquals) variableNameEquals.EqualsToken) OrElse
               targetToken.IsChildToken(Of WhereClauseSyntax)(Function(whereClause) whereClause.WhereKeyword) OrElse
               targetToken.IsChildToken(Of WhileStatementSyntax)(Function(whileStatement) whileStatement.WhileKeyword) OrElse
369
               targetToken.IsChildToken(Of WhileOrUntilClauseSyntax)(Function(whileUntilClause) whileUntilClause.WhileOrUntilKeyword) OrElse
P
Pilchie 已提交
370 371 372 373 374 375 376
               targetToken.IsChildToken(Of WithStatementSyntax)(Function(withStatement) withStatement.WithKeyword) OrElse
               targetToken.IsChildToken(Of XmlEmbeddedExpressionSyntax)(Function(xmlEmbeddedExpression) xmlEmbeddedExpression.LessThanPercentEqualsToken) OrElse
               targetToken.IsChildToken(Of YieldStatementSyntax)(Function(yieldStatement) yieldStatement.YieldKeyword) Then
                Return True
            End If

            ' The close paren of the parameter list of a single-line lambda?
377
            If targetToken.Kind = SyntaxKind.CloseParenToken AndAlso
378
               targetToken.Parent.IsKind(SyntaxKind.ParameterList) AndAlso
P
Pilchie 已提交
379 380 381 382 383
               TypeOf targetToken.Parent.Parent Is LambdaHeaderSyntax Then
                Return True
            End If

            ' A comma in a method call or collection initializer?
384
            If targetToken.Kind = SyntaxKind.CommaToken AndAlso
385
               targetToken.Parent.IsKind(SyntaxKind.ArgumentList, SyntaxKind.CollectionInitializer, SyntaxKind.EraseStatement) Then
P
Pilchie 已提交
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403

                Return True
            End If

            Return False
        End Function

        <Extension()>
        Public Function IsAttributeNameContext(syntaxTree As SyntaxTree, position As Integer, targetToken As SyntaxToken, cancellationToken As CancellationToken) As Boolean
            Contract.Requires(targetToken = syntaxTree.GetTargetToken(position, cancellationToken))
            Contract.Requires(Not (targetToken.IntersectsWith(position) AndAlso IsWord(targetToken)))

            If targetToken.IsChildToken(Function(a As AttributeTargetSyntax) a.ColonToken) OrElse
               targetToken.IsChildToken(Function(a As AttributeListSyntax) a.LessThanToken) OrElse
               targetToken.IsChildSeparatorToken(Function(a As AttributeListSyntax) a.Attributes) Then
                Return True
            End If

404 405 406
            If targetToken.IsKind(SyntaxKind.DotToken) AndAlso
               targetToken.Parent.IsKind(SyntaxKind.QualifiedName) AndAlso
               targetToken.Parent.Parent.IsKind(SyntaxKind.Attribute) Then
P
Pilchie 已提交
407 408 409 410 411 412 413 414 415 416 417 418
                Return True
            End If

            Return False
        End Function

        <Extension()>
        Public Function IsTypeContext(syntaxTree As SyntaxTree, position As Integer, token As SyntaxToken, cancellationToken As CancellationToken, Optional semanticModelOpt As SemanticModel = Nothing) As Boolean
            ' first do quick exit check
            If syntaxTree.IsInPreprocessorDirectiveContext(position, cancellationToken) OrElse
               syntaxTree.IsInInactiveRegion(position, cancellationToken) OrElse
               syntaxTree.IsEntirelyWithinComment(position, cancellationToken) OrElse
419
               syntaxTree.IsEntirelyWithinStringOrCharOrNumericLiteral(position, cancellationToken) Then
P
Pilchie 已提交
420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440

                Return False
            End If

            Contract.Requires(token = syntaxTree.GetTargetToken(position, cancellationToken))

            ' Types may start anywhere a full expression may be given
            If syntaxTree.IsExpressionContext(position, token, cancellationToken, semanticModelOpt) Then
                Return True
            End If

            ' Types may also start a statement
            If syntaxTree.IsSingleLineStatementContext(position, token, cancellationToken) Then
                Return True
            End If

            If syntaxTree.IsAttributeNameContext(position, token, cancellationToken) Then
                Return True
            End If

            ' Simple cases first
441
            If token.IsChildToken(Of ImportAliasClauseSyntax)(Function(importAliasClause) importAliasClause.EqualsToken) OrElse
P
Pilchie 已提交
442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476
               token.IsChildToken(Of ArrayCreationExpressionSyntax)(Function(arrayCreation) arrayCreation.NewKeyword) OrElse
               token.IsChildToken(Of AsNewClauseSyntax)(Function(asNewClause) asNewClause.NewExpression.NewKeyword) OrElse
               token.IsChildToken(Of InheritsStatementSyntax)(Function(node) node.InheritsKeyword) OrElse
               token.IsChildSeparatorToken(Of InheritsStatementSyntax, TypeSyntax)(Function(baseDeclaration) baseDeclaration.Types) OrElse
               token.IsChildToken(Of ImplementsStatementSyntax)(Function(node) node.ImplementsKeyword) OrElse
               token.IsChildSeparatorToken(Of ImplementsStatementSyntax, TypeSyntax)(Function(baseDeclaration) baseDeclaration.Types) OrElse
               token.IsChildToken(Of CastExpressionSyntax)(Function(castExpression) castExpression.CommaToken) OrElse
               token.IsChildToken(Of ImplementsClauseSyntax)(Function(implementsClause) implementsClause.ImplementsKeyword) OrElse
               token.IsChildSeparatorToken(Of ImplementsClauseSyntax, QualifiedNameSyntax)(Function(implementsClause) implementsClause.InterfaceMembers) OrElse
               token.IsChildToken(Of ImportsStatementSyntax)(Function(importsStatement) importsStatement.ImportsKeyword) OrElse
               token.IsChildSeparatorToken(Of ImportsStatementSyntax, ImportsClauseSyntax)(Function(importsStatement) importsStatement.ImportsClauses) OrElse
               token.IsChildToken(Of ObjectCreationExpressionSyntax)(Function(objectCreation) objectCreation.NewKeyword) OrElse
               token.IsChildToken(Of TypeArgumentListSyntax)(Function(typeArgumentList) typeArgumentList.OfKeyword) OrElse
               token.IsChildSeparatorToken(Of TypeArgumentListSyntax, TypeSyntax)(Function(typeArgumentList) typeArgumentList.Arguments) OrElse
               token.IsChildToken(Of TypeOfExpressionSyntax)(Function(typeOfIs) typeOfIs.OperatorToken) OrElse
               token.IsChildToken(Of TypeParameterSingleConstraintClauseSyntax)(Function(constraint) constraint.AsKeyword) OrElse
               token.IsChildToken(Of TypeParameterMultipleConstraintClauseSyntax)(Function(constraint) constraint.OpenBraceToken) OrElse
               token.IsChildSeparatorToken(Of TypeParameterMultipleConstraintClauseSyntax, ConstraintSyntax)(Function(constraint) constraint.Constraints) Then
                Return True
            End If

            Dim parent = token.Parent
            If parent Is Nothing Then
                Return False
            End If

            ' If we're in an Enum's underlying type, we never recommend...
            If parent.IsChildNode(Of EnumStatementSyntax)(Function(enumDeclaration) enumDeclaration.UnderlyingType) Then
                Return False
            End If

            ' ...otherwise any other SimpleAsClause is good
            Return token.IsChildToken(Of SimpleAsClauseSyntax)(Function(asClause) asClause.AsKeyword)
        End Function

J
jmarolf 已提交
477
        <Extension()>
478
        Public Function IsNameOfContext(syntaxTree As SyntaxTree, position As Integer, Optional cancellationToken As CancellationToken = Nothing) As Boolean
J
jmarolf 已提交
479 480 481 482
            ' first do quick exit check
            If syntaxTree.IsInPreprocessorDirectiveContext(position, cancellationToken) OrElse
               syntaxTree.IsInInactiveRegion(position, cancellationToken) OrElse
               syntaxTree.IsEntirelyWithinComment(position, cancellationToken) OrElse
483
               syntaxTree.IsEntirelyWithinStringOrCharOrNumericLiteral(position, cancellationToken) Then
J
jmarolf 已提交
484 485 486 487

                Return False
            End If

488 489 490
            Return syntaxTree _
                .GetTargetToken(position, cancellationToken) _
                .IsChildToken(Of NameOfExpressionSyntax)(Function(nameOfExpression) nameOfExpression.OpenParenToken)
J
jmarolf 已提交
491 492
        End Function

P
Pilchie 已提交
493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517
        <Extension()>
        Friend Function IsSingleLineStatementContext(syntaxTree As SyntaxTree, position As Integer, cancellationToken As CancellationToken) As Boolean
            Dim targetToken = syntaxTree.GetTargetToken(position, cancellationToken)
            Return IsSingleLineStatementContext(syntaxTree, position, targetToken, cancellationToken)
        End Function

        ''' <summary>
        ''' The specified position is where I could start a statement in a place where exactly one
        ''' statement could exist.
        ''' </summary>
        <Extension()>
        Friend Function IsSingleLineStatementContext(syntaxTree As SyntaxTree, position As Integer, targetToken As SyntaxToken, cancellationToken As CancellationToken) As Boolean
            ' We can always put single-statement constructs anywhere we can do a multi-statement
            ' construct
            If syntaxTree.IsMultiLineStatementStartContext(position, targetToken, cancellationToken) Then
                Return True
            End If

            Contract.Requires(targetToken = syntaxTree.GetTargetToken(position, cancellationToken))
            If targetToken.FollowsEndOfStatement(position) Then
                Return False
            End If

            ' We might be after a single-line statement lambda
            Dim statementLambdaHeader = targetToken.GetAncestor(Of LambdaHeaderSyntax)()
518
            If statementLambdaHeader IsNot Nothing AndAlso statementLambdaHeader.Parent.IsKind(SyntaxKind.SingleLineSubLambdaExpression,
P
Pilchie 已提交
519
                                                                                                    SyntaxKind.MultiLineSubLambdaExpression) Then
520
                Return statementLambdaHeader.ParameterList Is Nothing AndAlso targetToken = statementLambdaHeader.DeclarationKeyword OrElse
P
Pilchie 已提交
521 522 523 524 525 526
                       statementLambdaHeader.ParameterList IsNot Nothing AndAlso targetToken = statementLambdaHeader.ParameterList.CloseParenToken
            End If

            Return False
        End Function

P
Pharring 已提交
527
        ' PERF: Use UShort instead of SyntaxKind so the compiler can use array literal initialization.
B
beep boop 已提交
528
        Private ReadOnly s_multilineStatementBlockStartKinds As SyntaxKind() = DirectCast(New UShort() {
P
Pharring 已提交
529 530 531 532
            SyntaxKind.MultiLineFunctionLambdaExpression,
            SyntaxKind.MultiLineSubLambdaExpression,
            SyntaxKind.SubBlock,
            SyntaxKind.FunctionBlock,
533 534 535 536 537
            SyntaxKind.GetAccessorBlock,
            SyntaxKind.SetAccessorBlock,
            SyntaxKind.AddHandlerAccessorBlock,
            SyntaxKind.RemoveHandlerAccessorBlock,
            SyntaxKind.RaiseEventAccessorBlock,
P
Pharring 已提交
538 539 540 541
            SyntaxKind.ConstructorBlock,
            SyntaxKind.OperatorBlock
        }, SyntaxKind())

P
Pilchie 已提交
542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559
        ''' <summary>
        ''' The specified position is where I could start a statement in a place where one or more
        ''' statements could exist.
        ''' </summary>
        <Extension()>
        Friend Function IsMultiLineStatementStartContext(syntaxTree As SyntaxTree, position As Integer, targetToken As SyntaxToken, cancellationToken As CancellationToken) As Boolean
            If syntaxTree.IsInPreprocessorDirectiveContext(position, cancellationToken) OrElse
               syntaxTree.IsInSkippedText(position, cancellationToken) Then
                Return False
            End If

            ' There is one interesting exception to the code below: if it's the first statement inside
            ' a select case, then we can never have an executable statement
            If syntaxTree.IsStartOfSelectCaseBlock(position, targetToken, cancellationToken) Then
                Return False
            End If

            Contract.Requires(targetToken = syntaxTree.GetTargetToken(position, cancellationToken))
560
            If targetToken.Kind = SyntaxKind.None Then
P
Pilchie 已提交
561 562 563 564 565 566
                Return False
            End If

            ' We might be after the Then or Else of a single-line if statement
            Dim singleLineIf = targetToken.GetAncestor(Of SingleLineIfStatementSyntax)()
            If singleLineIf IsNot Nothing AndAlso
567 568
              (targetToken.IsChildToken(Of SingleLineIfStatementSyntax)(Function(n) n.ThenKeyword) OrElse
               targetToken.IsChildToken(Of SingleLineElseClauseSyntax)(Function(n) n.ElseKeyword)) Then
P
Pilchie 已提交
569 570 571 572 573 574 575 576 577 578 579

                Return True
            End If

            If Not targetToken.FollowsEndOfStatement(position) Then
                Return False
            End If

            Return syntaxTree.IsInStatementBlockOfKind(position,
                                                       targetToken,
                                                       cancellationToken,
B
beep boop 已提交
580
                                                       s_multilineStatementBlockStartKinds)
P
Pilchie 已提交
581 582 583 584 585 586 587 588 589 590 591 592 593
        End Function

        <Extension()>
        Friend Function IsStartOfSelectCaseBlock(syntaxTree As SyntaxTree, position As Integer, token As SyntaxToken, cancellationToken As CancellationToken) As Boolean
            Return syntaxTree.IsAfterStatementOfKind(position, token, cancellationToken, SyntaxKind.SelectStatement)
        End Function

        ''' <summary>
        ''' The specified position is immediately following a statement of one of the given kinds.
        ''' </summary>
        <Extension()>
        Friend Function IsAfterStatementOfKind(syntaxTree As SyntaxTree, position As Integer, targetToken As SyntaxToken, cancellationToken As CancellationToken, ParamArray kinds As SyntaxKind()) As Boolean
            Contract.Requires(targetToken = syntaxTree.GetTargetToken(position, cancellationToken))
594
            If targetToken.Kind = SyntaxKind.None OrElse targetToken.Parent Is Nothing Then
P
Pilchie 已提交
595 596 597 598 599 600 601
                Return False
            End If

            If Not targetToken.FollowsEndOfStatement(position) Then
                Return False
            End If

602
            Return targetToken.GetAncestor(Of StatementSyntax).IsKind(kinds)
P
Pilchie 已提交
603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619
        End Function

        <Extension()>
        Friend Function IsInStatementBlockOfKind(syntaxTree As SyntaxTree, position As Integer, targetToken As SyntaxToken, cancellationToken As CancellationToken, ParamArray kinds As SyntaxKind()) As Boolean
            Contract.Requires(targetToken = syntaxTree.GetTargetToken(position, cancellationToken))
            Dim ancestor = targetToken.Parent

            Do While ancestor IsNot Nothing
                If TypeOf ancestor Is EndBlockStatementSyntax Then
                    ' If we're within the End Block, skip the block itself
                    ancestor = ancestor.Parent.Parent

                    If ancestor Is Nothing Then
                        Return False
                    End If
                End If

620
                If ancestor.IsKind(kinds) Then
P
Pilchie 已提交
621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639
                    Return True
                End If

                If TypeOf ancestor Is LambdaExpressionSyntax Then
                    If Not (targetToken.FollowsEndOfStatement(position) AndAlso targetToken = ancestor.GetLastToken()) Then
                        ' We should not look past lambdas
                        Return False
                    End If
                End If

                ancestor = ancestor.Parent
            Loop

            Return False
        End Function

        <Extension()>
        Public Function IsQueryIntoClauseContext(syntaxTree As SyntaxTree, position As Integer, targetToken As SyntaxToken, cancellationToken As CancellationToken) As Boolean
            Contract.Requires(targetToken = syntaxTree.GetTargetToken(position, cancellationToken))
640
            If targetToken.Kind = SyntaxKind.None Then
P
Pilchie 已提交
641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658
                Return False
            End If

            If targetToken.Parent.FirstAncestorOrSelf(Of AggregateClauseSyntax)() Is Nothing AndAlso
               targetToken.Parent.FirstAncestorOrSelf(Of GroupByClauseSyntax)() Is Nothing AndAlso
               targetToken.Parent.FirstAncestorOrSelf(Of GroupJoinClauseSyntax)() Is Nothing Then
                Return False
            End If

            If targetToken.IsChildToken(Of AggregateClauseSyntax)(Function(a) a.IntoKeyword) OrElse
               targetToken.IsChildSeparatorToken(Of AggregateClauseSyntax, AggregationRangeVariableSyntax)(Function(a) a.AggregationVariables) OrElse
               targetToken.IsChildToken(Of GroupByClauseSyntax)(Function(g) g.IntoKeyword) OrElse
               targetToken.IsChildSeparatorToken(Of GroupByClauseSyntax, AggregationRangeVariableSyntax)(Function(g) g.AggregationVariables) OrElse
               targetToken.IsChildToken(Of GroupJoinClauseSyntax)(Function(g) g.IntoKeyword) OrElse
               targetToken.IsChildSeparatorToken(Of GroupJoinClauseSyntax, AggregationRangeVariableSyntax)(Function(g) g.AggregationVariables) Then
                Return True
            End If

659
            If targetToken.Kind = SyntaxKind.EqualsToken Then
P
Pilchie 已提交
660 661 662 663 664 665 666 667 668 669 670 671 672 673
                Dim aggregationRangeVariable = targetToken.GetAncestor(Of AggregationRangeVariableSyntax)()
                If aggregationRangeVariable IsNot Nothing AndAlso aggregationRangeVariable.NameEquals IsNot Nothing Then
                    If aggregationRangeVariable.NameEquals.EqualsToken = targetToken Then
                        Return True
                    End If
                End If
            End If

            Return False
        End Function

        <Extension()>
        Public Function IsRaiseEventContext(syntaxTree As SyntaxTree, position As Integer, targetToken As SyntaxToken, cancellationToken As CancellationToken) As Boolean
            Contract.Requires(targetToken = syntaxTree.GetTargetToken(position, cancellationToken))
674
            Return Not targetToken.FollowsEndOfStatement(position) AndAlso targetToken.Kind = SyntaxKind.RaiseEventKeyword
P
Pilchie 已提交
675 676 677 678 679 680 681 682 683 684 685 686
        End Function

        <Extension()>
        Public Function IsObjectCreationTypeContext(syntaxTree As SyntaxTree, position As Integer, cancellationToken As CancellationToken) As Boolean
            Dim targetToken = syntaxTree.GetTargetToken(position, cancellationToken)
            Return IsObjectCreationTypeContext(syntaxTree, position, targetToken, cancellationToken)
        End Function

        <Extension()>
        Public Function IsObjectCreationTypeContext(syntaxTree As SyntaxTree, position As Integer, targetToken As SyntaxToken, cancellationToken As CancellationToken) As Boolean
            Contract.Requires(targetToken = syntaxTree.GetTargetToken(position, cancellationToken))

687
            If Not targetToken.FollowsEndOfStatement(position) AndAlso targetToken.Kind = SyntaxKind.NewKeyword Then
P
Pilchie 已提交
688 689 690 691 692 693 694 695 696 697 698 699
                Return syntaxTree.IsTypeContext(position, targetToken, cancellationToken) OrElse
                       syntaxTree.IsMultiLineStatementStartContext(position, targetToken, cancellationToken) OrElse
                       syntaxTree.IsSingleLineStatementContext(position, targetToken, cancellationToken)
            End If

            Return False
        End Function

        <Extension>
        Friend Function IsEnumTypeMemberAccessContext(syntaxTree As SyntaxTree, position As Integer, targetToken As SyntaxToken, semanticModel As SemanticModel, cancellationToken As CancellationToken) As Boolean
            Contract.Requires(targetToken = syntaxTree.GetTargetToken(position, cancellationToken))

700 701
            If Not targetToken.IsKind(SyntaxKind.DotToken) OrElse
               Not targetToken.Parent.IsKind(SyntaxKind.SimpleMemberAccessExpression) Then
P
Pilchie 已提交
702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757

                Return False
            End If

            Dim memberAccess = DirectCast(targetToken.Parent, MemberAccessExpressionSyntax)

            Dim leftExpression = memberAccess.GetExpressionOfMemberAccessExpression()
            If leftExpression Is Nothing Then
                Return False
            End If

            Dim leftHandBinding = semanticModel.GetSymbolInfo(leftExpression)
            Dim symbol = leftHandBinding.GetBestOrAllSymbols().FirstOrDefault()

            If symbol Is Nothing Then
                Return False
            End If

            Select Case symbol.Kind
                Case SymbolKind.NamedType
                    Return DirectCast(symbol, INamedTypeSymbol).TypeKind = TypeKind.Enum
                Case SymbolKind.Alias
                    Dim target = DirectCast(symbol, IAliasSymbol).Target
                    Return target.IsType AndAlso DirectCast(target, ITypeSymbol).TypeKind = TypeKind.Enum
            End Select

            Return False
        End Function

        <Extension()>
        Friend Function IsFollowingCompleteExpression(Of TParent As SyntaxNode)(
            syntaxTree As SyntaxTree,
            position As Integer,
            targetToken As SyntaxToken,
            childGetter As Func(Of TParent, ExpressionSyntax),
            cancellationToken As CancellationToken,
            Optional allowImplicitLineContinuation As Boolean = True
        ) As Boolean

            Contract.Requires(targetToken = syntaxTree.GetTargetToken(position, cancellationToken))

            ' Check if our position begins a new statement
            If targetToken.MustBeginNewStatement(position) OrElse
                (targetToken.FollowsEndOfStatement(position) AndAlso Not allowImplicitLineContinuation) Then

                Return False
            End If

            For Each parent In targetToken.GetAncestors(Of TParent)()
                Dim expression = childGetter(parent)

                If expression Is Nothing Then
                    Continue For
                End If

                Dim terminatingToken = GetExpressionTerminatingToken(expression)
758
                If terminatingToken.Kind <> SyntaxKind.None AndAlso
P
Pilchie 已提交
759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802
                   Not terminatingToken.IsMissing AndAlso
                   terminatingToken = targetToken Then

                    Return True
                End If
            Next

            Return False
        End Function

        ''' <summary>
        ''' Given a syntax node, this returns the token that is the "end" token that ends this
        ''' expression.
        ''' </summary>
        ''' <param name="expression">The expression to get the last token of.</param>
        ''' <returns>The last token, or SyntaxKind.None if the last token is missing.</returns>
        Friend Function GetExpressionTerminatingToken(expression As SyntaxNode) As SyntaxToken
            Dim parenthesizedExpression = TryCast(expression, ParenthesizedExpressionSyntax)
            If parenthesizedExpression IsNot Nothing Then
                Return parenthesizedExpression.CloseParenToken
            End If

            Dim literalExpression = TryCast(expression, LiteralExpressionSyntax)
            If literalExpression IsNot Nothing Then
                Return literalExpression.Token
            End If

            Dim binaryExpression = TryCast(expression, BinaryExpressionSyntax)
            If binaryExpression IsNot Nothing Then
                Return GetExpressionTerminatingToken(binaryExpression.Right)
            End If

            Dim invocationExpression = TryCast(expression, InvocationExpressionSyntax)
            If invocationExpression IsNot Nothing AndAlso invocationExpression.ArgumentList IsNot Nothing Then
                Return invocationExpression.ArgumentList.CloseParenToken
            End If

            Dim memberAccessExpression = TryCast(expression, MemberAccessExpressionSyntax)
            If memberAccessExpression IsNot Nothing Then
                Return memberAccessExpression.Name.Identifier
            End If

            Dim functionAggregationExpression = TryCast(expression, FunctionAggregationSyntax)
            If functionAggregationExpression IsNot Nothing Then
803
                If functionAggregationExpression.OpenParenToken.Kind <> SyntaxKind.None Then
P
Pilchie 已提交
804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826
                    Return functionAggregationExpression.CloseParenToken
                Else
                    Return functionAggregationExpression.FunctionName
                End If
            End If

            Dim identifierName = TryCast(expression, IdentifierNameSyntax)
            If identifierName IsNot Nothing Then
                Return identifierName.Identifier
            End If

            Dim predefinedType = TryCast(expression, PredefinedTypeSyntax)
            If predefinedType IsNot Nothing Then
                Return predefinedType.Keyword
            End If

            Dim collectionInitializer = TryCast(expression, CollectionInitializerSyntax)
            If collectionInitializer IsNot Nothing Then
                Return collectionInitializer.CloseBraceToken
            End If

            Dim objectCreation = TryCast(expression, ObjectCreationExpressionSyntax)
            If objectCreation IsNot Nothing Then
827 828
                If objectCreation.ArgumentList IsNot Nothing Then
                    Return objectCreation.ArgumentList.CloseParenToken
829
                ElseIf objectCreation.Type.IsKind(SyntaxKind.QualifiedName) Then
830 831 832 833
                    Return DirectCast(objectCreation.Type, QualifiedNameSyntax).Right.GetLastToken()
                Else
                    Return objectCreation.Type.GetLastToken()
                End If
P
Pilchie 已提交
834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851
            End If

            Dim arrayCreation = TryCast(expression, ArrayCreationExpressionSyntax)
            If arrayCreation IsNot Nothing Then
                Return arrayCreation.Initializer.CloseBraceToken
            End If

            Dim unaryExpression = TryCast(expression, UnaryExpressionSyntax)
            If unaryExpression IsNot Nothing Then
                Return GetExpressionTerminatingToken(unaryExpression.Operand)
            End If

            Dim queryExpression = TryCast(expression, QueryExpressionSyntax)
            If queryExpression IsNot Nothing Then
                Return GetQueryClauseTerminatingToken(queryExpression.Clauses.Last())
            End If

            Dim singleLineLambda = TryCast(expression, SingleLineLambdaExpressionSyntax)
852
            If singleLineLambda IsNot Nothing AndAlso singleLineLambda.Kind = SyntaxKind.SingleLineFunctionLambdaExpression Then
P
Pilchie 已提交
853 854 855 856 857 858 859 860
                Dim bodyExpression = TryCast(singleLineLambda.Body, ExpressionSyntax)
                If bodyExpression IsNot Nothing Then
                    Return GetExpressionTerminatingToken(bodyExpression)
                End If
            End If

            Dim multiLineLambda = TryCast(expression, MultiLineLambdaExpressionSyntax)
            If multiLineLambda IsNot Nothing Then
861 862
                If multiLineLambda.EndSubOrFunctionStatement IsNot Nothing Then
                    Return multiLineLambda.EndSubOrFunctionStatement.BlockKeyword
P
Pilchie 已提交
863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896
                End If
            End If

            Dim anonymousObjectCreation = TryCast(expression, AnonymousObjectCreationExpressionSyntax)
            If anonymousObjectCreation IsNot Nothing Then
                If anonymousObjectCreation.Initializer IsNot Nothing Then
                    Return anonymousObjectCreation.Initializer.CloseBraceToken
                End If
            End If

            ' A SyntaxTokenStruct with Kind = None
            Return Nothing
        End Function

        Private Function GetQueryClauseTerminatingToken(queryClause As QueryClauseSyntax) As SyntaxToken
            Dim fromClause = TryCast(queryClause, FromClauseSyntax)
            If fromClause IsNot Nothing Then
                Return GetExpressionTerminatingToken(fromClause.Variables.LastCollectionExpression())
            End If

            Dim whereClause = TryCast(queryClause, WhereClauseSyntax)
            If whereClause IsNot Nothing Then
                Return GetExpressionTerminatingToken(whereClause.Condition)
            End If

            Dim letClause = TryCast(queryClause, LetClauseSyntax)
            If letClause IsNot Nothing Then
                Return GetExpressionTerminatingToken(letClause.Variables.LastRangeExpression())
            End If

            Dim orderByClause = TryCast(queryClause, OrderByClauseSyntax)
            If orderByClause IsNot Nothing Then
                Dim lastOrdering = orderByClause.Orderings.Last()

897
                If lastOrdering.AscendingOrDescendingKeyword.Kind = SyntaxKind.None Then
P
Pilchie 已提交
898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953
                    Return GetExpressionTerminatingToken(lastOrdering.Expression)
                Else
                    Return lastOrdering.AscendingOrDescendingKeyword
                End If
            End If

            Dim partitionWhileClause = TryCast(queryClause, PartitionWhileClauseSyntax)
            If partitionWhileClause IsNot Nothing Then
                Return GetExpressionTerminatingToken(partitionWhileClause.Condition)
            End If

            Dim partitionClause = TryCast(queryClause, PartitionClauseSyntax)
            If partitionClause IsNot Nothing Then
                Return GetExpressionTerminatingToken(partitionClause.Count)
            End If

            Dim aggregateClause = TryCast(queryClause, AggregateClauseSyntax)
            If aggregateClause IsNot Nothing Then
                If aggregateClause.AdditionalQueryOperators.Any() Then
                    Return GetQueryClauseTerminatingToken(aggregateClause.AdditionalQueryOperators.Last())
                Else
                    Return GetExpressionTerminatingToken(aggregateClause.Variables.LastCollectionExpression())
                End If
            End If

            Dim groupJoinClause = TryCast(queryClause, GroupJoinClauseSyntax)
            If groupJoinClause IsNot Nothing Then
                Return GetExpressionTerminatingToken(groupJoinClause.AggregationVariables.LastAggregation())
            End If

            Dim joinClause = TryCast(queryClause, SimpleJoinClauseSyntax)
            If joinClause IsNot Nothing Then
                Dim lastJoinCondition = joinClause.JoinConditions.LastOrDefault()

                If lastJoinCondition IsNot Nothing Then
                    Return GetExpressionTerminatingToken(lastJoinCondition.Right)
                Else
                    Return Nothing
                End If
            End If

            Dim groupByClause = TryCast(queryClause, GroupByClauseSyntax)
            If groupByClause IsNot Nothing Then
                Return GetExpressionTerminatingToken(groupByClause.AggregationVariables.LastAggregation())
            End If

            Dim selectClause = TryCast(queryClause, SelectClauseSyntax)
            If selectClause IsNot Nothing Then
                Return GetExpressionTerminatingToken(selectClause.Variables.LastRangeExpression())
            End If

            Dim distinctClause = TryCast(queryClause, DistinctClauseSyntax)
            If distinctClause IsNot Nothing Then
                Return distinctClause.DistinctKeyword
            End If

954
            Throw ExceptionUtilities.Unreachable
P
Pilchie 已提交
955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991
        End Function

        <Extension()>
        Friend Function LastCollectionExpression(collection As SeparatedSyntaxList(Of CollectionRangeVariableSyntax)) As ExpressionSyntax
            Dim lastCollectionRange = collection.LastOrDefault()

            If lastCollectionRange IsNot Nothing Then
                Return lastCollectionRange.Expression
            Else
                Return Nothing
            End If
        End Function

        <Extension()>
        Friend Function LastRangeExpression(collection As SeparatedSyntaxList(Of ExpressionRangeVariableSyntax)) As ExpressionSyntax
            Dim lastCollectionRange = collection.LastOrDefault()

            If lastCollectionRange IsNot Nothing Then
                Return lastCollectionRange.Expression
            Else
                Return Nothing
            End If
        End Function

        <Extension()>
        Friend Function LastAggregation(collection As SeparatedSyntaxList(Of AggregationRangeVariableSyntax)) As AggregationSyntax
            Dim lastCollectionRange = collection.LastOrDefault()

            If lastCollectionRange IsNot Nothing Then
                Return lastCollectionRange.Aggregation
            Else
                Return Nothing
            End If
        End Function

    End Module
End Namespace