File: Completion\KeywordRecommenders\Declarations\OfKeywordRecommender.vb
Web Access
Project: src\src\Features\VisualBasic\Portable\Microsoft.CodeAnalysis.VisualBasic.Features.vbproj (Microsoft.CodeAnalysis.VisualBasic.Features)
' 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 System.Collections.Immutable
Imports System.Threading
Imports Microsoft.CodeAnalysis.Completion.Providers
Imports Microsoft.CodeAnalysis.VisualBasic.Extensions.ContextQuery
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
 
Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.KeywordRecommenders.Declarations
 
    Friend Class OfKeywordRecommender
        Inherits AbstractKeywordRecommender
 
        Protected Overrides Function RecommendKeywords(context As VisualBasicSyntaxContext, cancellationToken As CancellationToken) As ImmutableArray(Of RecommendedKeyword)
            If context.FollowsEndOfStatement Then
                Return ImmutableArray(Of RecommendedKeyword).Empty
            End If
 
            Dim targetToken = context.TargetToken
            If Not targetToken.IsKind(SyntaxKind.OpenParenToken) Then
                Return ImmutableArray(Of RecommendedKeyword).Empty
            End If
 
            Dim methodDeclaration = targetToken.GetAncestor(Of MethodStatementSyntax)()
            If methodDeclaration IsNot Nothing Then
                If methodDeclaration.TypeParameterList IsNot Nothing Then
                    If targetToken = methodDeclaration.TypeParameterList.OpenParenToken Then
                        Return ImmutableArray.Create(New RecommendedKeyword("Of", VBFeaturesResources.Identifies_a_type_parameter_on_a_generic_class_structure_interface_delegate_or_procedure))
                    End If
                ElseIf methodDeclaration.ParameterList IsNot Nothing Then
                    ' If we don't have a TypeParametersOpt, then we might be in a place where it's ambiguous where we are.
                    ' For example, typing Sub Goo(|, it's not clear if that's a TypeParameters block I'm in or a regular
                    ' block. The parser chooses the sane choice of calling that a regular parameters block until it knows
                    ' otherwise.
                    If targetToken = methodDeclaration.ParameterList.OpenParenToken Then
                        Return ImmutableArray.Create(New RecommendedKeyword("Of", VBFeaturesResources.Identifies_a_type_parameter_on_a_generic_class_structure_interface_delegate_or_procedure))
                    End If
                End If
            End If
 
            Dim implementsClause = targetToken.GetAncestor(Of ImplementsClauseSyntax)
            If implementsClause IsNot Nothing Then
                If targetToken.IsKind(SyntaxKind.OpenParenToken) AndAlso targetToken.Parent.IsKind(SyntaxKind.TypeArgumentList) Then
                    Return ImmutableArray.Create(New RecommendedKeyword("Of", VBFeaturesResources.Identifies_a_type_parameter_on_a_generic_class_structure_interface_delegate_or_procedure))
                End If
            End If
 
            Dim inheritsStatement = targetToken.GetAncestor(Of InheritsStatementSyntax)
            If inheritsStatement IsNot Nothing Then
                If targetToken.IsKind(SyntaxKind.OpenParenToken) AndAlso targetToken.Parent.IsKind(SyntaxKind.TypeArgumentList) Then
                    Return ImmutableArray.Create(New RecommendedKeyword("Of", VBFeaturesResources.Identifies_a_type_parameter_on_a_generic_class_structure_interface_delegate_or_procedure))
                End If
            End If
 
            Dim delegateDeclaration = targetToken.GetAncestor(Of DelegateStatementSyntax)()
            If delegateDeclaration IsNot Nothing Then
                If delegateDeclaration.TypeParameterList IsNot Nothing Then
                    If targetToken = delegateDeclaration.TypeParameterList.OpenParenToken Then
                        Return ImmutableArray.Create(New RecommendedKeyword("Of", VBFeaturesResources.Identifies_a_type_parameter_on_a_generic_class_structure_interface_delegate_or_procedure))
                    End If
                ElseIf delegateDeclaration.ParameterList IsNot Nothing Then
                    If targetToken = delegateDeclaration.ParameterList.OpenParenToken Then
                        Return ImmutableArray.Create(New RecommendedKeyword("Of", VBFeaturesResources.Identifies_a_type_parameter_on_a_generic_class_structure_interface_delegate_or_procedure))
                    End If
                End If
            End If
 
            Dim typeDeclaration = targetToken.GetAncestor(Of TypeStatementSyntax)()
            If typeDeclaration IsNot Nothing AndAlso typeDeclaration.IsKind(SyntaxKind.ClassStatement, SyntaxKind.InterfaceStatement, SyntaxKind.StructureStatement) Then
                If typeDeclaration.TypeParameterList IsNot Nothing Then
                    If targetToken = typeDeclaration.TypeParameterList.OpenParenToken Then
                        Return ImmutableArray.Create(New RecommendedKeyword("Of", VBFeaturesResources.Identifies_a_type_parameter_on_a_generic_class_structure_interface_delegate_or_procedure))
                    End If
                End If
            End If
 
            ' Cases:
            '  Dim f As New Goo(|
            '  Goo(|
            Dim argumentList = targetToken.GetAncestor(Of ArgumentListSyntax)()
            If argumentList IsNot Nothing Then
                ' Ensure it's not "Dim F(|" unless creating a generic delegate
                If Not argumentList.HasAncestor(Of ModifiedIdentifierSyntax)() AndAlso
                   targetToken = argumentList.OpenParenToken AndAlso
                   (Not context.IsDelegateCreationContext() OrElse IsGenericDelegateCreationExpression(targetToken, context.SemanticModel, cancellationToken)) Then
                    Return ImmutableArray.Create(New RecommendedKeyword("Of", VBFeaturesResources.Identifies_a_type_parameter_on_a_generic_class_structure_interface_delegate_or_procedure))
                End If
            End If
 
            ' Case: Dim f As Goo(|
            Dim arrayRankSpecifier = targetToken.GetAncestor(Of ArrayRankSpecifierSyntax)()
            If arrayRankSpecifier IsNot Nothing Then
                If targetToken = arrayRankSpecifier.OpenParenToken Then
                    Dim arrayType = TryCast(arrayRankSpecifier.Parent, ArrayTypeSyntax)
                    If arrayType IsNot Nothing AndAlso IsPartiallyTypedGenericName(arrayType.ElementType, context.SemanticModel) Then
                        Return ImmutableArray.Create(New RecommendedKeyword("Of", VBFeaturesResources.Identifies_a_type_parameter_on_a_generic_class_structure_interface_delegate_or_procedure))
                    End If
                End If
            End If
 
            Return ImmutableArray(Of RecommendedKeyword).Empty
        End Function
 
        Private Shared Function IsPartiallyTypedGenericName(type As TypeSyntax, semanticModel As SemanticModel) As Boolean
            Dim symbols = semanticModel.LookupNamespacesAndTypes(
                position:=type.SpanStart,
                name:=type.ToString())
 
            Return symbols.OfType(Of INamedTypeSymbol)() _
                          .Where(Function(nt) nt.IsGenericType) _
                          .Any()
        End Function
 
        Private Shared Function IsGenericDelegateCreationExpression(token As SyntaxToken, semanticModel As SemanticModel, cancellationToken As CancellationToken) As Boolean
            Dim objectCreationExpression = token.GetAncestor(Of ObjectCreationExpressionSyntax)()
            If objectCreationExpression IsNot Nothing Then
                Dim type = objectCreationExpression.Type
                If type IsNot Nothing Then
                    Return semanticModel.LookupName(type, namespacesAndTypesOnly:=True, cancellationToken:=cancellationToken) _
                                        .OfType(Of INamedTypeSymbol)() _
                                        .Where(Function(nt) nt.IsDelegateType AndAlso nt.Arity > 0) _
                                        .Any()
                End If
            End If
 
            Return False
        End Function
    End Class
End Namespace