|
' 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
Imports Microsoft.CodeAnalysis.LanguageService
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic
Partial Friend Class VisualBasicTypeInferenceService
Private Class TypeInferrer
Inherits AbstractTypeInferrer
Public Sub New(semanticModel As SemanticModel, cancellationToken As CancellationToken)
MyBase.New(semanticModel, cancellationToken)
End Sub
Protected Overrides Function IsUnusableType(otherSideType As ITypeSymbol) As Boolean
Return otherSideType.IsErrorType() AndAlso
otherSideType.Name = String.Empty
End Function
Protected Overrides Function GetTypes_DoNotCallDirectly(node As SyntaxNode, objectAsDefault As Boolean) As IEnumerable(Of TypeInferenceInfo)
If node IsNot Nothing Then
Dim info = SemanticModel.GetTypeInfo(node)
If info.Type IsNot Nothing AndAlso info.Type.TypeKind <> TypeKind.Error Then
Return CreateResult(info.Type)
End If
If info.ConvertedType IsNot Nothing AndAlso info.ConvertedType.TypeKind <> TypeKind.Error Then
Return CreateResult(info.ConvertedType)
End If
If node.Kind = SyntaxKind.AddressOfExpression Then
Dim unaryExpression = DirectCast(node, UnaryExpressionSyntax)
Dim symbol = SemanticModel.GetSymbolInfo(unaryExpression.Operand, CancellationToken).GetAnySymbol()
Dim type = symbol.ConvertToType(Me.Compilation)
If type IsNot Nothing Then
Return CreateResult(type)
End If
End If
End If
Return If(objectAsDefault, CreateResult(Me.Compilation.ObjectType), SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)())
End Function
Protected Overrides Function InferTypesWorker_DoNotCallDirectly(node As SyntaxNode) As IEnumerable(Of TypeInferenceInfo)
Dim expression = TryCast(node, ExpressionSyntax)
If expression IsNot Nothing Then
expression = expression.WalkUpParentheses()
node = expression
End If
Dim parent = node.Parent
Return parent.TypeSwitch(
Function(addRemoveHandlerStatement As AddRemoveHandlerStatementSyntax) InferTypeInAddRemoveHandlerStatementSyntax(addRemoveHandlerStatement, expression),
Function(argument As ArgumentSyntax) InferTypeInArgument(argument),
Function(arrayCreationExpression As ArrayCreationExpressionSyntax) InferTypeInArrayCreationExpression(arrayCreationExpression),
Function(arrayRank As ArrayRankSpecifierSyntax) InferTypeInArrayRankSpecifier(),
Function(arrayType As ArrayTypeSyntax) InferTypeInArrayType(arrayType),
Function(asClause As AsClauseSyntax) InferTypeInAsClause(asClause, expression),
Function(assignmentStatement As AssignmentStatementSyntax) InferTypeInAssignmentStatement(assignmentStatement, expression),
Function(attribute As AttributeSyntax) InferTypeInAttribute(),
Function(awaitExpression As AwaitExpressionSyntax) InferTypeInAwaitExpression(awaitExpression),
Function(binaryExpression As BinaryExpressionSyntax) InferTypeInBinaryExpression(binaryExpression, expression),
Function(callStatement As CallStatementSyntax) InferTypeInCallStatement(),
Function(castExpression As CastExpressionSyntax) InferTypeInCastExpression(castExpression, expression),
Function(catchFilterClause As CatchFilterClauseSyntax) InferTypeInCatchFilterClause(catchFilterClause),
Function(collectionInitializer As CollectionInitializerSyntax) InferTypeInCollectionInitializerExpression(collectionInitializer, expression),
Function(conditionalAccessExpression As ConditionalAccessExpressionSyntax) InferTypeInConditionalAccessExpression(conditionalAccessExpression),
Function(conditionalExpression As BinaryConditionalExpressionSyntax) InferTypeInBinaryConditionalExpression(conditionalExpression, expression),
Function(conditionalExpression As TernaryConditionalExpressionSyntax) InferTypeInTernaryConditionalExpression(conditionalExpression, expression),
Function(doStatement As DoStatementSyntax) InferTypeInDoStatement(),
Function(equalsValue As EqualsValueSyntax) InferTypeInEqualsValue(equalsValue),
Function(expressionStatement As ExpressionStatementSyntax) InferTypeInExpressionStatement(expressionStatement),
Function(forEachStatement As ForEachStatementSyntax) InferTypeInForEachStatement(forEachStatement, expression),
Function(forStatement As ForStatementSyntax) InferTypeInForStatement(forStatement, expression),
Function(forStepClause As ForStepClauseSyntax) InferTypeInForStepClause(forStepClause),
Function(ifStatement As ElseIfStatementSyntax) InferTypeInIfOrElseIfStatement(),
Function(ifStatement As IfStatementSyntax) InferTypeInIfOrElseIfStatement(),
Function(memberAccessExpression As MemberAccessExpressionSyntax) InferTypeInMemberAccessExpression(memberAccessExpression, expression),
Function(namedFieldInitializer As NamedFieldInitializerSyntax) InferTypeInNamedFieldInitializer(namedFieldInitializer),
Function(parenthesizedLambda As MultiLineLambdaExpressionSyntax) InferTypeInLambda(parenthesizedLambda),
Function(prefixUnary As UnaryExpressionSyntax) InferTypeInUnaryExpression(prefixUnary),
Function(returnStatement As ReturnStatementSyntax) InferTypeForReturnStatement(returnStatement),
Function(ifStatement As SingleLineIfStatementSyntax) InferTypeInIfOrElseIfStatement(),
Function(singleLineLambdaExpression As SingleLineLambdaExpressionSyntax) InferTypeInLambda(singleLineLambdaExpression),
Function(switchStatement As SelectStatementSyntax) InferTypeInSelectStatement(switchStatement),
Function(throwStatement As ThrowStatementSyntax) InferTypeInThrowStatement(),
Function(typeOfExpression As TypeOfExpressionSyntax) InferTypeInTypeOfExpressionSyntax(typeOfExpression),
Function(usingStatement As UsingStatementSyntax) InferTypeInUsingStatement(),
Function(whileStatement As WhileOrUntilClauseSyntax) InferTypeInWhileOrUntilClause(),
Function(whileStatement As WhileStatementSyntax) InferTypeInWhileStatement(),
Function(yieldStatement As YieldStatementSyntax) InferTypeInYieldStatement(yieldStatement),
Function(x) SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)())
End Function
Private Function InferTypeInTypeOfExpressionSyntax(typeOfExpression As TypeOfExpressionSyntax) As IEnumerable(Of TypeInferenceInfo)
Dim expressionType = typeOfExpression.Type
If expressionType Is Nothing Then
Return SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)()
End If
Dim typeSymbol = SemanticModel.GetTypeInfo(expressionType).Type
If TypeOf typeSymbol IsNot INamedTypeSymbol Then
Return SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)()
End If
Return CreateResult(typeSymbol)
End Function
Private Function InferTypeInAddRemoveHandlerStatementSyntax(addRemoveHandlerStatement As AddRemoveHandlerStatementSyntax,
expression As ExpressionSyntax) As IEnumerable(Of TypeInferenceInfo)
If expression Is addRemoveHandlerStatement.DelegateExpression Then
Return GetTypes(addRemoveHandlerStatement.EventExpression)
End If
Return SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)()
End Function
Protected Overrides Function InferTypesWorker_DoNotCallDirectly(position As Integer) As IEnumerable(Of TypeInferenceInfo)
Dim tree = Me.SemanticModel.SyntaxTree
Dim token = tree.FindTokenOnLeftOfPosition(position, CancellationToken)
token = token.GetPreviousTokenIfTouchingWord(position)
Dim parent = token.Parent
Return parent.TypeSwitch(
Function(argument As ArgumentSyntax) InferTypeInArgument(argument, previousToken:=token),
Function(argumentList As ArgumentListSyntax) InferTypeInArgumentList(argumentList, previousToken:=token),
Function(arrayCreationExpression As ArrayCreationExpressionSyntax) InferTypeInArrayCreationExpression(arrayCreationExpression),
Function(arrayRank As ArrayRankSpecifierSyntax) InferTypeInArrayRankSpecifier(),
Function(arrayType As ArrayTypeSyntax) InferTypeInArrayType(arrayType),
Function(asClause As AsClauseSyntax) InferTypeInAsClause(asClause, previousToken:=token),
Function(assignmentStatement As AssignmentStatementSyntax) InferTypeInAssignmentStatement(assignmentStatement, previousToken:=token),
Function(attribute As AttributeSyntax) InferTypeInAttribute(),
Function(awaitExpression As AwaitExpressionSyntax) InferTypeInAwaitExpression(awaitExpression),
Function(binaryExpression As BinaryExpressionSyntax) InferTypeInBinaryExpression(binaryExpression, previousToken:=token),
Function(callStatement As CallStatementSyntax) InferTypeInCallStatement(),
Function(caseStatement As CaseStatementSyntax) InferTypeInCaseStatement(caseStatement),
Function(castExpression As CastExpressionSyntax) InferTypeInCastExpression(castExpression),
Function(catchFilterClause As CatchFilterClauseSyntax) InferTypeInCatchFilterClause(catchFilterClause, previousToken:=token),
Function(collectionInitializer As CollectionInitializerSyntax) InferTypeInCollectionInitializerExpression(collectionInitializer, previousToken:=token),
Function(conditionalExpression As BinaryConditionalExpressionSyntax) InferTypeInBinaryConditionalExpression(conditionalExpression, previousToken:=token),
Function(conditionalExpression As TernaryConditionalExpressionSyntax) InferTypeInTernaryConditionalExpression(conditionalExpression, previousToken:=token),
Function(doStatement As DoStatementSyntax) InferTypeInDoStatement(),
Function(equalsValue As EqualsValueSyntax) InferTypeInEqualsValue(equalsValue),
Function(expressionStatement As ExpressionStatementSyntax) InferTypeInExpressionStatement(expressionStatement),
Function(forEachStatement As ForEachStatementSyntax) InferTypeInForEachStatement(forEachStatement, previousToken:=token),
Function(forStatement As ForStatementSyntax) InferTypeInForStatement(forStatement, previousToken:=token),
Function(forStepClause As ForStepClauseSyntax) InferTypeInForStepClause(forStepClause, token),
Function(ifStatement As IfStatementSyntax) InferTypeInIfOrElseIfStatement(),
Function(memberAccessExpression As MemberAccessExpressionSyntax) InferTypeInMemberAccessExpression(memberAccessExpression, previousToken:=token),
Function(nameColonEquals As NameColonEqualsSyntax) InferTypeInArgumentList(TryCast(nameColonEquals.Parent.Parent, ArgumentListSyntax), DirectCast(nameColonEquals.Parent, ArgumentSyntax)),
Function(namedFieldInitializer As NamedFieldInitializerSyntax) InferTypeInNamedFieldInitializer(namedFieldInitializer),
Function(objectCreation As ObjectCreationExpressionSyntax) InferTypes(objectCreation),
Function(parameterListSyntax As ParameterListSyntax) InferTypeInParameterList(parameterListSyntax),
Function(parenthesizedLambda As MultiLineLambdaExpressionSyntax) InferTypeInLambda(parenthesizedLambda),
Function(prefixUnary As UnaryExpressionSyntax) InferTypeInUnaryExpression(prefixUnary),
Function(returnStatement As ReturnStatementSyntax) InferTypeForReturnStatement(returnStatement, token),
Function(singleLineLambdaExpression As SingleLineLambdaExpressionSyntax) InferTypeInLambda(singleLineLambdaExpression),
Function(switchStatement As SelectStatementSyntax) InferTypeInSelectStatement(switchStatement),
Function(throwStatement As ThrowStatementSyntax) InferTypeInThrowStatement(),
Function(tupleExpression As TupleExpressionSyntax) InferTypeInTupleExpression(tupleExpression, token),
Function(usingStatement As UsingStatementSyntax) InferTypeInUsingStatement(),
Function(whileStatement As WhileOrUntilClauseSyntax) InferTypeInWhileOrUntilClause(),
Function(whileStatement As WhileStatementSyntax) InferTypeInWhileStatement(),
Function(yieldStatement As YieldStatementSyntax) InferTypeInYieldStatement(yieldStatement, token),
Function(x) SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)())
End Function
Private Function InferTypeInParameterList(parameterList As ParameterListSyntax) As IEnumerable(Of TypeInferenceInfo)
Return If(parameterList.Parent IsNot Nothing,
InferTypeInLambda(TryCast(parameterList.Parent.Parent, LambdaExpressionSyntax)),
SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)())
End Function
Private Function InferTypeInArgument(argument As ArgumentSyntax,
Optional previousToken As SyntaxToken = Nothing) As IEnumerable(Of TypeInferenceInfo)
If TypeOf argument.Parent Is ArgumentListSyntax Then
Return InferTypeInArgumentList(
DirectCast(argument.Parent, ArgumentListSyntax), argument, previousToken)
End If
If TypeOf argument.Parent Is TupleExpressionSyntax Then
Return InferTypeInTupleExpression(
DirectCast(argument.Parent, TupleExpressionSyntax),
DirectCast(argument, SimpleArgumentSyntax))
End If
Return SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)
End Function
Private Function InferTypeInTupleExpression(
tupleExpression As TupleExpressionSyntax,
previousToken As SyntaxToken) As IEnumerable(Of TypeInferenceInfo)
If previousToken = tupleExpression.OpenParenToken Then
Return InferTypeInTupleExpression(tupleExpression, tupleExpression.Arguments(0))
ElseIf previousToken.IsKind(SyntaxKind.CommaToken) Then
Dim argsAndCommas = tupleExpression.Arguments.GetWithSeparators()
Dim commaIndex = argsAndCommas.IndexOf(previousToken)
Return InferTypeInTupleExpression(tupleExpression, DirectCast(argsAndCommas(commaIndex + 1).AsNode(), SimpleArgumentSyntax))
End If
Return SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)()
End Function
Private Function InferTypeInTupleExpression(
tupleExpression As TupleExpressionSyntax,
argument As SimpleArgumentSyntax) As IEnumerable(Of TypeInferenceInfo)
Dim index = tupleExpression.Arguments.IndexOf(argument)
Dim parentTypes = InferTypes(tupleExpression)
Return parentTypes.Select(Function(TypeInfo) TypeInfo.InferredType).
OfType(Of INamedTypeSymbol)().
Where(Function(namedType) namedType.IsTupleType AndAlso index < namedType.TupleElements.Length).
Select(Function(tupleType) New TypeInferenceInfo(tupleType.TupleElements(index).Type))
End Function
Private Function InferTypeInArgumentList(argumentList As ArgumentListSyntax,
Optional argumentOpt As ArgumentSyntax = Nothing,
Optional previousToken As SyntaxToken = Nothing) As IEnumerable(Of TypeInferenceInfo)
If argumentList Is Nothing Then
Return SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)()
End If
If argumentList.Parent IsNot Nothing Then
If argumentList.IsParentKind(SyntaxKind.ArrayCreationExpression) Then
Return CreateResult(Compilation.GetSpecialType(SpecialType.System_Int32))
ElseIf argumentList.IsParentKind(SyntaxKind.InvocationExpression) Then
Dim invocation = TryCast(argumentList.Parent, InvocationExpressionSyntax)
Dim index As Integer = 0
If argumentOpt IsNot Nothing Then
index = invocation.ArgumentList.Arguments.IndexOf(argumentOpt)
Else
index = GetArgumentListIndex(argumentList, previousToken)
End If
If index < 0 Then
Return SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)()
End If
Dim info = SemanticModel.GetSymbolInfo(invocation)
' Check all the methods that have at least enough arguments to support being
' called with argument at this position. Note: if they're calling an extension
' method then it will need one more argument in order for us to call it.
Dim symbols = info.GetBestOrAllSymbols()
' Special case If this Is an Then argument In Enum.HasFlag, infer the Enum type that we're invoking into,
' as otherwise we infer "Enum" which isn't useful
If symbols.Any(AddressOf IsEnumHasFlag) Then
Dim memberAccess = TryCast(invocation.Expression, MemberAccessExpressionSyntax)
If memberAccess IsNot Nothing Then
Dim typeInfo = SemanticModel.GetTypeInfo(memberAccess.Expression, CancellationToken)
If typeInfo.Type IsNot Nothing AndAlso typeInfo.Type.IsEnumType() Then
Return CreateResult(typeInfo.Type)
End If
End If
End If
If symbols.Any() Then
Return InferTypeInArgument(argumentOpt, index, symbols)
Else
' It may be an array access
Dim targetExpression As ExpressionSyntax = Nothing
If invocation.Expression IsNot Nothing Then
targetExpression = invocation.Expression
ElseIf invocation.Parent.IsKind(SyntaxKind.ConditionalAccessExpression) Then
targetExpression = DirectCast(invocation.Parent, ConditionalAccessExpressionSyntax).Expression
End If
If targetExpression IsNot Nothing Then
Dim expressionType = SemanticModel.GetTypeInfo(targetExpression)
If TypeOf expressionType.Type Is IArrayTypeSymbol Then
Return CreateResult(Compilation.GetSpecialType(SpecialType.System_Int32))
End If
End If
End If
ElseIf argumentList.IsParentKind(SyntaxKind.ObjectCreationExpression) Then
' new Outer(Goo());
'
' new Outer(a: Goo());
'
' etc.
Dim creation = TryCast(argumentList.Parent, ObjectCreationExpressionSyntax)
Dim info = SemanticModel.GetSymbolInfo(creation.Type)
Dim namedType = TryCast(info.Symbol, INamedTypeSymbol)
If namedType IsNot Nothing Then
If namedType.TypeKind = TypeKind.Delegate Then
Return CreateResult(namedType)
Else
Dim index As Integer = 0
If argumentOpt IsNot Nothing Then
index = creation.ArgumentList.Arguments.IndexOf(argumentOpt)
Else
index = GetArgumentListIndex(argumentList, previousToken)
End If
If index < 0 Then
Return SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)()
End If
Dim constructors = namedType.InstanceConstructors.Where(Function(m) m.Parameters.Length > index)
Return InferTypeInArgument(argumentOpt, index, constructors)
End If
End If
ElseIf argumentList.IsParentKind(SyntaxKind.Attribute) Then
' Ex: <SomeAttribute(here)>
Dim attribute = TryCast(argumentList.Parent, AttributeSyntax)
If argumentOpt IsNot Nothing AndAlso argumentOpt.IsNamed Then
Return GetTypes(DirectCast(argumentOpt, SimpleArgumentSyntax).NameColonEquals.Name)
End If
Dim index As Integer = 0
If argumentOpt IsNot Nothing Then
index = attribute.ArgumentList.Arguments.IndexOf(argumentOpt)
Else
index = GetArgumentListIndex(argumentList, previousToken)
End If
If index < 0 Then
Return SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)()
End If
Dim info = SemanticModel.GetSymbolInfo(attribute)
Dim symbols = info.GetBestOrAllSymbols()
If symbols.Any() Then
Dim methods = symbols.OfType(Of IMethodSymbol)()
Return InferTypeInArgument(argumentOpt, index, methods)
End If
#If False Then
ElseIf argument.Parent.IsParentKind(SyntaxKind.ElementAccessExpression) Then
' Outer[Goo()];
'
' Outer[a: Goo()];
'
' etc.
Dim elementAccess = TryCast(argument.Parent.Parent, ElementAccessExpressionSyntax)
Dim info = Me.Binding.GetSemanticInfo(elementAccess.Expression)
If TypeOf info.Type Is ArrayTypeSymbol Then
Return Me.Compilation.GetSpecialType(SpecialType.System_Int32)
Else
Dim [type] = TryCast(info.Type, NamedTypeSymbol)
Dim index = elementAccess.ArgumentList.Arguments.IndexOf(argument)
Dim indexer = [type].GetMembers().OfType().Where(Function(p) p.IsIndexer).Where(Function(p) p.Parameters.Count > index).FirstOrDefault()
If indexer IsNot Nothing Then
Return InferTypeInArgument(argument, index, SpecializedCollections.SingletonEnumerable(indexer.Parameters))
End If
End If
#End If
End If
End If
Return SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)()
End Function
Private Shared Function InferTypeInArgument(argument As ArgumentSyntax, index As Integer, symbols As IEnumerable(Of ISymbol)) As IEnumerable(Of TypeInferenceInfo)
Dim methods = symbols.OfType(Of IMethodSymbol)()
If methods.Any() Then
Dim parameters = methods.Select(Function(m) m.Parameters)
Return InferTypeInArgument(argument, index, parameters)
End If
Dim properties = symbols.OfType(Of IPropertySymbol)()
If properties.Any() Then
Dim parameters = properties.Select(Function(p) p.Parameters)
Return InferTypeInArgument(argument, index, parameters)
End If
Return SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)()
End Function
Private Shared Function InferTypeInArgument(
argument As ArgumentSyntax,
index As Integer,
parameterizedSymbols As IEnumerable(Of ImmutableArray(Of IParameterSymbol))) As IEnumerable(Of TypeInferenceInfo)
Dim simpleArgument = TryCast(argument, SimpleArgumentSyntax)
If simpleArgument IsNot Nothing AndAlso simpleArgument.IsNamed Then
Dim parameters = parameterizedSymbols _
.SelectMany(Function(m) m) _
.Where(Function(p) p.Name = simpleArgument.NameColonEquals.Name.Identifier.ValueText)
Return parameters.Select(Function(p) New TypeInferenceInfo(p.Type, p.IsParams))
Else
' Otherwise, just take the first overload and pick what type this parameter is
' based on index.
Return parameterizedSymbols.Where(Function(a) index < a.Length).Select(Function(a) New TypeInferenceInfo(a(index).Type, a(index).IsParams))
End If
Return SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)()
End Function
Private Function InferTypeInArrayCreationExpression(arrayCreationExpression As ArrayCreationExpressionSyntax) As IEnumerable(Of TypeInferenceInfo)
Dim outerTypes = InferTypes(arrayCreationExpression)
Return outerTypes.Where(Function(c) TypeOf c.InferredType Is IArrayTypeSymbol) _
.Select(Function(c) New TypeInferenceInfo(DirectCast(c.InferredType, IArrayTypeSymbol).ElementType))
End Function
Private Function InferTypeInArrayRankSpecifier() As IEnumerable(Of TypeInferenceInfo)
Return CreateResult(Me.Compilation.GetSpecialType(SpecialType.System_Int32))
End Function
Private Function InferTypeInArrayType(arrayType As ArrayTypeSyntax) As IEnumerable(Of TypeInferenceInfo)
' Bind the array type, then unwrap whatever we get back based on the number of rank
' specifiers we see.
Dim currentTypes = InferTypes(arrayType)
Dim i = 0
While i < arrayType.RankSpecifiers.Count
currentTypes = currentTypes.WhereAsArray(Function(c) TypeOf c.InferredType Is IArrayTypeSymbol).
SelectAsArray(Function(c) New TypeInferenceInfo(DirectCast(c.InferredType, IArrayTypeSymbol).ElementType))
i = i + 1
End While
Return currentTypes
End Function
Private Function InferTypeInAsClause(asClause As AsClauseSyntax,
Optional expressionOpt As ExpressionSyntax = Nothing,
Optional previousToken As SyntaxToken = Nothing) As IEnumerable(Of TypeInferenceInfo)
If previousToken <> Nothing AndAlso previousToken.Kind <> SyntaxKind.AsKeyword Then
Return SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)()
End If
If asClause.IsParentKind(SyntaxKind.CatchStatement) Then
If expressionOpt Is asClause.Type OrElse previousToken.Kind = SyntaxKind.AsKeyword Then
Return CreateResult(Me.Compilation.ExceptionType)
End If
End If
Return SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)()
End Function
Private Function InferTypeInAssignmentStatement(assignmentStatement As AssignmentStatementSyntax,
Optional expressionOpt As ExpressionSyntax = Nothing,
Optional previousToken As SyntaxToken = Nothing) As IEnumerable(Of TypeInferenceInfo)
If assignmentStatement.IsKind(SyntaxKind.LeftShiftAssignmentStatement) OrElse
assignmentStatement.IsKind(SyntaxKind.RightShiftAssignmentStatement) Then
Return CreateResult(Me.Compilation.GetSpecialType(SpecialType.System_Int32))
End If
If expressionOpt Is assignmentStatement.Right OrElse previousToken = assignmentStatement.OperatorToken Then
Return GetTypes(assignmentStatement.Left)
End If
If expressionOpt Is assignmentStatement.Left Then
Return GetTypes(assignmentStatement.Right)
End If
Return SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)()
End Function
Private Function InferTypeInAttribute() As IEnumerable(Of TypeInferenceInfo)
Return CreateResult(Me.Compilation.AttributeType)
End Function
Private Function InferTypeInAwaitExpression(awaitExpression As AwaitExpressionSyntax) As IEnumerable(Of TypeInferenceInfo)
' await <expression>
Dim types = InferTypes(awaitExpression, filterUnusable:=False)
Dim task = Me.Compilation.GetTypeByMetadataName(GetType(Task).FullName)
Dim taskOfT = Me.Compilation.GetTypeByMetadataName(GetType(Task(Of)).FullName)
If task Is Nothing OrElse taskOfT Is Nothing Then
Return SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)()
End If
If Not types.Any() Then
Return CreateResult(task)
End If
Return types.Select(Function(t) New TypeInferenceInfo(If(t.InferredType.SpecialType = SpecialType.System_Void, task, taskOfT.Construct(t.InferredType))))
End Function
Private Function InferTypeInConditionalAccessExpression(conditional As ConditionalAccessExpressionSyntax) As IEnumerable(Of TypeInferenceInfo)
Return InferTypes(conditional)
End Function
Private Function InferTypeInBinaryConditionalExpression(conditional As BinaryConditionalExpressionSyntax,
Optional expressionOpt As ExpressionSyntax = Nothing,
Optional previousToken As SyntaxToken = Nothing) As IEnumerable(Of TypeInferenceInfo)
If previousToken <> Nothing AndAlso previousToken <> conditional.OpenParenToken AndAlso previousToken <> conditional.CommaToken Then
Return SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)()
End If
If conditional.FirstExpression Is expressionOpt OrElse previousToken = conditional.OpenParenToken Then
Dim rightTypes = GetTypes(conditional.SecondExpression, objectAsDefault:=True)
' value type : If (Goo(), 0)
' otherwise : If (Goo(), "")
Return rightTypes.Select(Function(t) If(t.InferredType.IsValueType,
New TypeInferenceInfo(Me.Compilation.GetSpecialType(SpecialType.System_Nullable_T).Construct(t.InferredType)),
t))
Else
Dim leftTypes = GetTypes(conditional.FirstExpression)
Return leftTypes.Select(Function(t)
If t.InferredType.OriginalDefinition.SpecialType = SpecialType.System_Nullable_T Then
Return New TypeInferenceInfo(DirectCast(t.InferredType, INamedTypeSymbol).TypeArguments(0))
Else
Return t
End If
End Function)
End If
End Function
Private Function InferTypeInBinaryExpression(binop As BinaryExpressionSyntax,
Optional expression As ExpressionSyntax = Nothing,
Optional previousToken As SyntaxToken = Nothing) As IEnumerable(Of TypeInferenceInfo)
' If we got a token, it must be the operator in the binary expression
Contract.ThrowIfTrue(previousToken <> Nothing AndAlso binop.OperatorToken <> previousToken)
Dim rightSide = previousToken <> Nothing OrElse expression Is binop.Right
Select Case binop.OperatorToken.Kind
Case SyntaxKind.LessThanLessThanToken,
SyntaxKind.GreaterThanGreaterThanToken,
SyntaxKind.LessThanLessThanEqualsToken,
SyntaxKind.GreaterThanGreaterThanEqualsToken
If rightSide Then
Return CreateResult(Me.Compilation.GetSpecialType(SpecialType.System_Int32))
End If
End Select
' Try to figure out what's on the other size of the binop. If we can, then just that
' type. This is often a reasonable heuristics to use for most operators. NOTE(cyrusn):
' we could try to bind the token to see what overloaded operators it corresponds to.
' But the gain is pretty marginal IMO.
Dim otherSide = If(rightSide, binop.Left, binop.Right)
Dim otherSideTypes = GetTypes(otherSide)
If otherSideTypes.Any(Function(t) t.InferredType.SpecialType <> SpecialType.System_Object AndAlso Not t.InferredType.IsErrorType()) Then
Return otherSideTypes
End If
Select Case binop.OperatorToken.Kind
Case SyntaxKind.CaretToken,
SyntaxKind.AmpersandToken,
SyntaxKind.LessThanToken,
SyntaxKind.LessThanEqualsToken,
SyntaxKind.GreaterThanToken,
SyntaxKind.GreaterThanEqualsToken,
SyntaxKind.PlusToken,
SyntaxKind.MinusToken,
SyntaxKind.AsteriskToken,
SyntaxKind.SlashToken,
SyntaxKind.CaretEqualsToken,
SyntaxKind.PlusEqualsToken,
SyntaxKind.MinusEqualsToken,
SyntaxKind.AsteriskEqualsToken,
SyntaxKind.SlashEqualsToken,
SyntaxKind.LessThanLessThanToken,
SyntaxKind.GreaterThanGreaterThanToken,
SyntaxKind.LessThanLessThanEqualsToken,
SyntaxKind.GreaterThanGreaterThanEqualsToken
Return CreateResult(Me.Compilation.GetSpecialType(SpecialType.System_Int32))
Case SyntaxKind.AndKeyword,
SyntaxKind.AndAlsoKeyword,
SyntaxKind.OrKeyword,
SyntaxKind.OrElseKeyword
Return CreateResult(SpecialType.System_Boolean)
End Select
Return SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)()
End Function
Private Function InferTypeInCastExpression(castExpression As CastExpressionSyntax,
Optional expressionOpt As ExpressionSyntax = Nothing) As IEnumerable(Of TypeInferenceInfo)
If castExpression.Expression Is expressionOpt Then
Return SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)()
End If
Return GetTypes(castExpression.Type)
End Function
Private Function InferTypeInCatchFilterClause(catchFilterClause As CatchFilterClauseSyntax, Optional previousToken As SyntaxToken = Nothing) As IEnumerable(Of TypeInferenceInfo)
If previousToken <> Nothing AndAlso previousToken <> catchFilterClause.WhenKeyword Then
Return SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)()
End If
Return CreateResult(SpecialType.System_Boolean)
End Function
Private Function InferTypeInDoStatement() As IEnumerable(Of TypeInferenceInfo)
Return CreateResult(SpecialType.System_Boolean)
End Function
Private Function InferTypeInEqualsValue(equalsValue As EqualsValueSyntax) As IEnumerable(Of TypeInferenceInfo)
If equalsValue.IsParentKind(SyntaxKind.VariableDeclarator) Then
Dim variableDeclarator = DirectCast(equalsValue.Parent, VariableDeclaratorSyntax)
If variableDeclarator.AsClause Is Nothing AndAlso variableDeclarator.IsParentKind(SyntaxKind.UsingStatement) Then
Return CreateResult(SpecialType.System_IDisposable)
End If
If variableDeclarator.Names.Count >= 1 Then
Dim name = variableDeclarator.Names(0)
Dim symbol = SemanticModel.GetDeclaredSymbol(name, CancellationToken)
If symbol IsNot Nothing Then
Select Case symbol.Kind
Case SymbolKind.Field
Return CreateResult(DirectCast(symbol, IFieldSymbol).Type)
Case SymbolKind.Local
Return CreateResult(DirectCast(symbol, ILocalSymbol).Type)
End Select
End If
End If
If TypeOf variableDeclarator.AsClause Is SimpleAsClauseSyntax Then
Dim asClause = DirectCast(variableDeclarator.AsClause, SimpleAsClauseSyntax)
Return GetTypes(asClause.Type)
End If
ElseIf equalsValue.IsParentKind(SyntaxKind.PropertyStatement) Then
Dim propertySyntax = CType(equalsValue.Parent, PropertyStatementSyntax)
Dim propertySymbol = SemanticModel.GetDeclaredSymbol(propertySyntax)
If propertySymbol Is Nothing Then
Return SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)()
End If
Return CreateResult(propertySymbol.Type)
End If
Return SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)()
End Function
Private Function InferTypeInExpressionStatement(expressionStatement As ExpressionStatementSyntax) As IEnumerable(Of TypeInferenceInfo)
If expressionStatement.Expression.IsKind(SyntaxKind.InvocationExpression) Then
Return InferTypeInCallStatement()
End If
Return SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)()
End Function
Private Function InferTypeInCallStatement() As IEnumerable(Of TypeInferenceInfo)
Return CreateResult(SpecialType.System_Void)
End Function
Private Function InferTypeInForEachStatement(forEachStatement As ForEachStatementSyntax,
Optional expressionOpt As ExpressionSyntax = Nothing,
Optional previousToken As SyntaxToken = Nothing) As IEnumerable(Of TypeInferenceInfo)
If expressionOpt Is forEachStatement.Expression OrElse previousToken = forEachStatement.InKeyword Then
If TypeOf forEachStatement.ControlVariable Is VariableDeclaratorSyntax Then
Dim declarator = DirectCast(forEachStatement.ControlVariable, VariableDeclaratorSyntax)
If TypeOf declarator.AsClause Is SimpleAsClauseSyntax Then
Dim variableTypes = GetTypes(DirectCast(declarator.AsClause, SimpleAsClauseSyntax).Type, objectAsDefault:=True)
Dim type = Me.Compilation.GetSpecialType(SpecialType.System_Collections_Generic_IEnumerable_T)
Return variableTypes.Select(Function(t) New TypeInferenceInfo(type.Construct(t.InferredType)))
End If
ElseIf TypeOf forEachStatement.ControlVariable Is SimpleNameSyntax Then
Dim type = Me.Compilation.GetSpecialType(SpecialType.System_Collections_Generic_IEnumerable_T)
Return CreateResult(type.Construct(Compilation.GetSpecialType(SpecialType.System_Object)))
End If
End If
Return SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)()
End Function
Private Function InferTypeInForStatement(forStatement As ForStatementSyntax,
Optional expressionOpt As ExpressionSyntax = Nothing,
Optional previousToken As SyntaxToken = Nothing) As IEnumerable(Of TypeInferenceInfo)
If (expressionOpt IsNot Nothing AndAlso expressionOpt IsNot forStatement.ControlVariable) OrElse
previousToken = forStatement.ToKeyword OrElse
previousToken = forStatement.EqualsToken Then
If TypeOf forStatement.ControlVariable Is VariableDeclaratorSyntax Then
Dim declarator = DirectCast(forStatement.ControlVariable, VariableDeclaratorSyntax)
If TypeOf declarator.AsClause Is SimpleAsClauseSyntax Then
Return GetTypes(DirectCast(declarator.AsClause, SimpleAsClauseSyntax).Type, objectAsDefault:=True)
End If
End If
End If
Return SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)()
End Function
#Disable Warning IDE0060 ' Remove unused parameter
' TODO(cyrusn): Potentially infer a different type based on the type of the variable
' being foreach-ed over.
Private Function InferTypeInForStepClause(forStepClause As ForStepClauseSyntax, Optional previousToken As SyntaxToken = Nothing) As IEnumerable(Of TypeInferenceInfo)
#Enable Warning IDE0060 ' Remove unused parameter
Return CreateResult(Me.Compilation.GetSpecialType(SpecialType.System_Int32))
End Function
Private Function InferTypeInIfOrElseIfStatement() As IEnumerable(Of TypeInferenceInfo)
Return CreateResult(SpecialType.System_Boolean)
End Function
Private Function InferTypeInLambda(lambda As ExpressionSyntax) As IEnumerable(Of TypeInferenceInfo)
If lambda Is Nothing Then
Return SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)()
End If
' Func<int,string> = i => Goo();
Dim lambdaTypes = GetTypes(lambda).Where(IsUsableTypeFunc)
If lambdaTypes.IsEmpty() Then
lambdaTypes = InferTypes(lambda)
End If
Return lambdaTypes.Where(Function(t) t.InferredType.TypeKind = TypeKind.Delegate).SelectMany(Function(t) t.InferredType.GetMembers(WellKnownMemberNames.DelegateInvokeName).OfType(Of IMethodSymbol)().Select(Function(m) New TypeInferenceInfo(m.ReturnType)))
Return SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)()
End Function
Private Function InferTypeForReturnStatement(returnStatement As ReturnStatementSyntax, Optional previousToken As SyntaxToken = Nothing) As IEnumerable(Of TypeInferenceInfo)
' If we're position based, we must have gotten the Return token
If previousToken <> Nothing AndAlso previousToken.Kind <> SyntaxKind.ReturnKeyword Then
Return SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)()
End If
' If we're in a lambda, then use the return type of the lambda to figure out what to
' infer. i.e. Func<int,string> f = i => { return Goo(); }
Dim lambda = returnStatement.GetAncestorsOrThis(Of ExpressionSyntax)().FirstOrDefault(
Function(e) TypeOf e Is MultiLineLambdaExpressionSyntax OrElse
TypeOf e Is SingleLineLambdaExpressionSyntax)
If lambda IsNot Nothing Then
Return InferTypeInLambda(lambda)
End If
If returnStatement.GetAncestorOrThis(Of MethodBlockBaseSyntax) Is Nothing Then
Return SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)
End If
Dim memberSymbol = GetDeclaredMemberSymbolFromOriginalSemanticModel(SemanticModel, returnStatement.GetAncestor(Of MethodBlockBaseSyntax).BlockStatement)
Dim memberMethod = TryCast(memberSymbol, IMethodSymbol)
If memberMethod IsNot Nothing Then
If memberMethod.IsAsync Then
Dim typeArguments = memberMethod.ReturnType.GetTypeArguments()
Dim taskOfT = Me.Compilation.TaskOfTType()
Return If(
taskOfT IsNot Nothing AndAlso Equals(memberMethod.ReturnType.OriginalDefinition, taskOfT) AndAlso typeArguments.Any(),
CreateResult(typeArguments.First()),
SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo))
Else
Return CreateResult(memberMethod.ReturnType)
End If
End If
Return SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)()
End Function
Private Function InferTypeInYieldStatement(yieldStatement As YieldStatementSyntax, Optional previoustoken As SyntaxToken = Nothing) As IEnumerable(Of TypeInferenceInfo)
' If we're position based, we must be after the Yield token
If previoustoken <> Nothing AndAlso Not previoustoken.IsKind(SyntaxKind.YieldKeyword) Then
Return SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)
End If
If yieldStatement.GetAncestorOrThis(Of MethodBlockBaseSyntax) Is Nothing Then
Return SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)
End If
Dim memberSymbol = GetDeclaredMemberSymbolFromOriginalSemanticModel(SemanticModel, yieldStatement.GetAncestor(Of MethodBlockBaseSyntax).BlockStatement)
Dim memberType = If(TryCast(memberSymbol, IMethodSymbol)?.ReturnType,
TryCast(memberSymbol, IPropertySymbol)?.Type)
If TypeOf memberType Is INamedTypeSymbol Then
If memberType.OriginalDefinition.SpecialType = SpecialType.System_Collections_Generic_IEnumerable_T OrElse
memberType.OriginalDefinition.SpecialType = SpecialType.System_Collections_Generic_IEnumerator_T Then
Return CreateResult(DirectCast(memberType, INamedTypeSymbol).TypeArguments(0))
End If
End If
Return SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)
End Function
Private Function GetDeclaredMemberSymbolFromOriginalSemanticModel(currentSemanticModel As SemanticModel, declarationInCurrentTree As DeclarationStatementSyntax) As ISymbol
Dim originalSemanticModel = currentSemanticModel.GetOriginalSemanticModel()
Dim declaration As DeclarationStatementSyntax
If currentSemanticModel.IsSpeculativeSemanticModel Then
Dim tokenInOriginalTree = originalSemanticModel.SyntaxTree.GetRoot(CancellationToken).FindToken(currentSemanticModel.OriginalPositionForSpeculation)
declaration = tokenInOriginalTree.GetAncestor(Of DeclarationStatementSyntax)
Else
declaration = declarationInCurrentTree
End If
Return originalSemanticModel.GetDeclaredSymbol(declaration, CancellationToken)
End Function
Private Function InferTypeInSelectStatement(switchStatementSyntax As SelectStatementSyntax) As IEnumerable(Of TypeInferenceInfo)
' Use the first case label to determine the return type.
If TypeOf switchStatementSyntax.Parent Is SelectBlockSyntax Then
Dim firstCase = DirectCast(switchStatementSyntax.Parent, SelectBlockSyntax).CaseBlocks.SelectMany(Function(c) c.CaseStatement.Cases).OfType(Of SimpleCaseClauseSyntax).FirstOrDefault()
If firstCase IsNot Nothing Then
Return GetTypes(firstCase.Value)
End If
End If
Return CreateResult(Me.Compilation.GetSpecialType(SpecialType.System_Int32))
End Function
Private Function InferTypeInTernaryConditionalExpression(conditional As TernaryConditionalExpressionSyntax,
Optional expressionOpt As ExpressionSyntax = Nothing,
Optional previousToken As SyntaxToken = Nothing) As IEnumerable(Of TypeInferenceInfo)
If previousToken <> Nothing AndAlso previousToken.Kind <> SyntaxKind.OpenParenToken AndAlso previousToken.Kind <> SyntaxKind.CommaToken Then
Return SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)()
ElseIf previousToken = conditional.OpenParenToken Then
Return CreateResult(SpecialType.System_Boolean)
ElseIf previousToken = conditional.FirstCommaToken Then
Return GetTypes(conditional.WhenTrue)
ElseIf previousToken = conditional.SecondCommaToken Then
Return GetTypes(conditional.WhenFalse)
End If
If conditional.Condition Is expressionOpt Then
Return CreateResult(SpecialType.System_Boolean)
Else
Return If(conditional.WhenTrue Is expressionOpt, GetTypes(conditional.WhenFalse), GetTypes(conditional.WhenTrue))
End If
End Function
Private Function InferTypeInThrowStatement(Optional previousToken As SyntaxToken = Nothing) As IEnumerable(Of TypeInferenceInfo)
' If we're not the Throw token, there's nothing to do
If previousToken <> Nothing AndAlso previousToken.Kind <> SyntaxKind.ThrowKeyword Then
Return SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)()
End If
Return CreateResult(Me.Compilation.ExceptionType)
End Function
Private Function InferTypeInUnaryExpression(unaryExpressionSyntax As UnaryExpressionSyntax) As IEnumerable(Of TypeInferenceInfo)
Select Case unaryExpressionSyntax.Kind
Case SyntaxKind.UnaryPlusExpression, SyntaxKind.UnaryMinusExpression
Return CreateResult(Me.Compilation.GetSpecialType(SpecialType.System_Int32))
Case SyntaxKind.NotExpression
Dim types = InferTypes(unaryExpressionSyntax)
If types.Any(Function(t) t.InferredType.IsNumericType) Then
Return types.Where(Function(t) t.InferredType.IsNumericType)
End If
Return CreateResult(SpecialType.System_Boolean)
Case SyntaxKind.AddressOfExpression
Return InferTypes(unaryExpressionSyntax)
End Select
Return SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)()
End Function
Private Function InferTypeInUsingStatement() As IEnumerable(Of TypeInferenceInfo)
Return CreateResult(SpecialType.System_IDisposable)
End Function
Private Function InferTypeInVariableDeclarator(variableDeclarator As VariableDeclaratorSyntax) As IEnumerable(Of TypeInferenceInfo)
If variableDeclarator.AsClause IsNot Nothing Then
Return SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)()
End If
Return GetTypes(variableDeclarator.AsClause.Type)
End Function
Private Function InferTypeInWhileStatement() As IEnumerable(Of TypeInferenceInfo)
Return CreateResult(SpecialType.System_Boolean)
End Function
Private Function InferTypeInWhileOrUntilClause() As IEnumerable(Of TypeInferenceInfo)
Return CreateResult(SpecialType.System_Boolean)
End Function
Private Function InferTypeInMemberAccessExpression(
memberAccessExpression As MemberAccessExpressionSyntax,
Optional expressionOpt As ExpressionSyntax = Nothing,
Optional previousToken As SyntaxToken? = Nothing) As IEnumerable(Of TypeInferenceInfo)
' We need to be on the right of the dot to infer an appropriate type for
' the member access expression. i.e. if we have "Goo.Bar" then we can
' def infer what the type of 'Bar' should be (it's whatever type we infer
' for 'Goo.Bar' itself. However, if we're on 'Goo' then we can't figure
' out anything about its type.
If previousToken <> Nothing Then
If previousToken.Value <> memberAccessExpression.OperatorToken Then
Return SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)
End If
Return InferTypes(memberAccessExpression)
Else
' If we're on the left side of a dot, it's possible in a few cases
' to figure out what type we should be. Specifically, if we have
'
' await goo.ConfigureAwait()
'
' then we can figure out what 'goo' should be based on the await
' context.
If expressionOpt Is memberAccessExpression.Expression Then
Return InferTypeForExpressionOfMemberAccessExpression(memberAccessExpression)
End If
Return InferTypes(memberAccessExpression)
End If
End Function
Private Function InferTypeForExpressionOfMemberAccessExpression(memberAccessExpression As MemberAccessExpressionSyntax) As IEnumerable(Of TypeInferenceInfo)
Dim name = memberAccessExpression.Name.Identifier.Value
If name.Equals(NameOf(Task(Of Integer).ConfigureAwait)) AndAlso
memberAccessExpression.IsParentKind(SyntaxKind.InvocationExpression) AndAlso
memberAccessExpression.Parent.IsParentKind(SyntaxKind.AwaitExpression) Then
Return InferTypes(DirectCast(memberAccessExpression.Parent, ExpressionSyntax))
ElseIf name.Equals(NameOf(Task(Of Integer).ContinueWith)) Then
' goo.ContinueWith(...)
' We want to infer Task<T>. For now, we'll just do Task<object>,
' in the future it would be nice to figure out the actual result
' type based on the argument to ContinueWith.
Dim taskOfT = Me.Compilation.TaskOfTType()
If taskOfT IsNot Nothing Then
Return CreateResult(taskOfT.Construct(Me.Compilation.ObjectType))
End If
ElseIf name.Equals(NameOf(Enumerable.Select)) OrElse
name.Equals(NameOf(Enumerable.Where)) Then
Dim ienumerableType = Me.Compilation.IEnumerableOfTType()
' goo.Select
' We want to infer IEnumerable<T>. We can try to figure out what
' T if we get a delegate as the first argument to Select/Where.
If ienumerableType IsNot Nothing AndAlso memberAccessExpression.IsParentKind(SyntaxKind.InvocationExpression) Then
Dim invocation = DirectCast(memberAccessExpression.Parent, InvocationExpressionSyntax)
If invocation.ArgumentList IsNot Nothing AndAlso invocation.ArgumentList.Arguments.Count > 0 AndAlso
TypeOf invocation.ArgumentList.Arguments(0) Is SimpleArgumentSyntax Then
Dim argumentExpression = DirectCast(invocation.ArgumentList.Arguments(0), SimpleArgumentSyntax).Expression
Dim argumentTypes = GetTypes(argumentExpression)
Dim delegateType = argumentTypes.FirstOrDefault().InferredType.GetDelegateType(Me.Compilation)
Dim typeArg = If(delegateType?.TypeArguments.Length > 0,
delegateType.TypeArguments(0),
Me.Compilation.ObjectType)
If delegateType Is Nothing OrElse IsUnusableType(typeArg) Then
If TypeOf argumentExpression Is LambdaExpressionSyntax Then
typeArg = If(InferTypeForFirstParameterOfLambda(DirectCast(argumentExpression, LambdaExpressionSyntax)),
Me.Compilation.ObjectType)
End If
End If
Return CreateResult(ienumerableType.Construct(typeArg))
End If
End If
End If
Return SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)()
End Function
Private Function InferTypeForFirstParameterOfLambda(
lambda As LambdaExpressionSyntax) As ITypeSymbol
If lambda.SubOrFunctionHeader.ParameterList.Parameters.Count > 0 Then
Dim parameter = lambda.SubOrFunctionHeader.ParameterList.Parameters(0)
Dim parameterName = parameter.Identifier.Identifier.ValueText
If TypeOf lambda Is SingleLineLambdaExpressionSyntax Then
Dim singleLine = DirectCast(lambda, SingleLineLambdaExpressionSyntax)
Return InferTypeForFirstParameterOfLambda(parameterName, singleLine.Body)
ElseIf TypeOf lambda Is MultiLineLambdaExpressionSyntax Then
Dim multiLine = DirectCast(lambda, MultiLineLambdaExpressionSyntax)
For Each statement In multiLine.Statements
Dim type = InferTypeForFirstParameterOfLambda(parameterName, statement)
If type IsNot Nothing Then
Return type
End If
Next
End If
End If
Return Nothing
End Function
Private Function InferTypeForFirstParameterOfLambda(
parameterName As String, node As SyntaxNode) As ITypeSymbol
If node.IsKind(SyntaxKind.IdentifierName) Then
Dim identifier = DirectCast(node, IdentifierNameSyntax)
If CaseInsensitiveComparison.Equals(parameterName, identifier.Identifier.ValueText) AndAlso
SemanticModel.GetSymbolInfo(identifier.Identifier).Symbol?.Kind = SymbolKind.Parameter Then
Return InferTypes(identifier).FirstOrDefault().InferredType
End If
Else
For Each child In node.ChildNodesAndTokens()
If child.IsNode Then
Dim type = InferTypeForFirstParameterOfLambda(parameterName, child.AsNode)
If type IsNot Nothing Then
Return type
End If
End If
Next
End If
Return Nothing
End Function
Private Function InferTypeInNamedFieldInitializer(initializer As NamedFieldInitializerSyntax) As IEnumerable(Of TypeInferenceInfo)
Dim right = SemanticModel.GetTypeInfo(initializer.Name).Type
If right IsNot Nothing AndAlso TypeOf right IsNot IErrorTypeSymbol Then
Return CreateResult(right)
End If
Return CreateResult(SemanticModel.GetTypeInfo(initializer.Expression).Type)
End Function
Public Function InferTypeInCaseStatement(caseStatement As CaseStatementSyntax) As IEnumerable(Of TypeInferenceInfo)
Dim selectBlock = caseStatement.GetAncestor(Of SelectBlockSyntax)()
If selectBlock IsNot Nothing Then
Return GetTypes(selectBlock.SelectStatement.Expression)
End If
Return SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)()
End Function
Private Shared Function GetArgumentListIndex(argumentList As ArgumentListSyntax, previousToken As SyntaxToken) As Integer
If previousToken = argumentList.OpenParenToken Then
Return 0
End If
Dim index = argumentList.Arguments.GetWithSeparators().IndexOf(previousToken)
Return If(index >= 0, (index + 1) \ 2, -1)
End Function
Private Function InferTypeInCollectionInitializerExpression(
collectionInitializer As CollectionInitializerSyntax,
Optional expression As ExpressionSyntax = Nothing,
Optional previousToken As SyntaxToken = Nothing) As IEnumerable(Of TypeInferenceInfo)
' New List(Of T) From { x }
If expression IsNot Nothing Then
Dim expressionAddMethodSymbols = SemanticModel.GetCollectionInitializerSymbolInfo(expression).GetAllSymbols().OfType(Of IMethodSymbol)
Dim expressionAddMethodParameterTypes = expressionAddMethodSymbols.
Where(Function(m) m.Parameters.Length = 1).
Select(Function(m) New TypeInferenceInfo(m.Parameters(0).Type))
If expressionAddMethodParameterTypes.Any() Then
Return expressionAddMethodParameterTypes
End If
' New Dictionary<K,V> From { { x, ... } }
Dim parameterIndex = collectionInitializer.Initializers.IndexOf(expression)
Dim initializerAddMethodSymbols = SemanticModel.GetCollectionInitializerSymbolInfo(collectionInitializer).GetAllSymbols().OfType(Of IMethodSymbol)
Dim initializerAddMethodParameterTypes = initializerAddMethodSymbols.
Where(Function(m) m.Parameters.Length = collectionInitializer.Initializers.Count).
Select(Function(m) m.Parameters.ElementAtOrDefault(parameterIndex)?.Type).
WhereNotNull().
Select(Function(m) New TypeInferenceInfo(m))
If initializerAddMethodParameterTypes.Any() Then
Return initializerAddMethodParameterTypes
End If
End If
' New List(of T) From { $$
If previousToken.Kind() = SyntaxKind.OpenBraceToken OrElse
previousToken.Kind() = SyntaxKind.CommaToken Then
Dim objectInitializer = TryCast(collectionInitializer.Parent, ObjectCollectionInitializerSyntax)
Dim objectCreation = TryCast(objectInitializer?.Parent, ObjectCreationExpressionSyntax)
If objectCreation IsNot Nothing Then
Dim types = GetTypes(objectCreation).Select(Function(t) t.InferredType)
Return types.OfType(Of INamedTypeSymbol)().SelectMany(Function(t) GetCollectionElementType(t))
End If
End If
Return SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)
End Function
End Class
End Class
End Namespace
|