提交 6bdb5a53 编写于 作者: C CyrusNajmabadi

VB side of type inference.

上级 f29a6cb0
......@@ -70,7 +70,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax
End Function
End Class
Public Partial Class SingleLineLambdaExpressionSyntax
Partial Public Class SingleLineLambdaExpressionSyntax
''' <summary>
''' Single line subs only have a single statement. However, when binding it is convenient to have a statement list. For example,
''' dim statements are not valid in a single line lambda. However, it is nice to be able to provide semantic info about the local.
......
......@@ -1820,7 +1820,6 @@ static void Foo(System.ConsoleModifiers arg)
}
[Fact, Trait(Traits.Feature, Traits.Features.TypeInferenceService)]
[WorkItem(6765, "https://github.com/dotnet/roslyn/issues/6765")]
public async Task TestWhereCall()
{
var text =
......@@ -1837,7 +1836,6 @@ void Foo()
}
[Fact, Trait(Traits.Feature, Traits.Features.TypeInferenceService)]
[WorkItem(6765, "https://github.com/dotnet/roslyn/issues/6765")]
public async Task TestWhereCall2()
{
var text =
......
......@@ -705,7 +705,7 @@ Module M
Dim x As Boolean = Await [|F|].ContinueWith(Function(a) True).ContinueWith(Function(a) False)
End Sub
End Module"
Await TestAsync(text, "System.Object", testPosition:=False)
Await TestAsync(text, "Global.System.Threading.Tasks.Task(Of System.Object)", testPosition:=False)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.TypeInferenceService)>
......@@ -736,5 +736,31 @@ End Module"
End Class"
Await TestAsync(text, "System.Object", testNode:=False, testPosition:=True)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.TypeInferenceService)>
Public Async Function TestWhereCall() As Task
Dim text =
"imports System.Collections.Generic
class C
sub Foo()
[|ints|].Where(function(i) i > 10)
end sub
end class"
Await TestAsync(text, "Global.System.Collections.Generic.IEnumerable(Of System.Int32)", testPosition:=False)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.TypeInferenceService)>
Public Async Function TestWhereCall2() As Task
Dim text =
"imports System.Collections.Generic
class C
sub Foo()
[|ints|].Where(function(i)
return i > 10
end function)
end sub
end class"
Await TestAsync(text, "Global.System.Collections.Generic.IEnumerable(Of System.Int32)", testPosition:=False)
End Function
End Class
End Namespace
......@@ -1457,72 +1457,78 @@ private IEnumerable<ITypeSymbol> InferTypeInNameColon(NameColonSyntax nameColon,
Debug.Assert(expressionOpt != null);
if (expressionOpt == memberAccessExpression.Expression)
{
// 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 foo.ConfigureAwait()
//
// then we can figure out what 'foo' should be based on teh await
// context.
var name = memberAccessExpression.Name.Identifier.Value;
if (name.Equals(nameof(Task<int>.ConfigureAwait)) &&
memberAccessExpression.IsParentKind(SyntaxKind.InvocationExpression) &&
memberAccessExpression.Parent.IsParentKind(SyntaxKind.AwaitExpression))
{
return InferTypes((ExpressionSyntax)memberAccessExpression.Parent);
}
else if (name.Equals(nameof(Task<int>.ContinueWith)))
return InferTypeForExpressionOfMemberAccessExpression(memberAccessExpression);
}
// We're right after the dot in "Foo.Bar". The type for "Bar" should be
// whatever type we'd infer for "Foo.Bar" itself.
return InferTypes(memberAccessExpression);
}
}
private IEnumerable<ITypeSymbol> InferTypeForExpressionOfMemberAccessExpression(
MemberAccessExpressionSyntax memberAccessExpression)
{
// 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 foo.ConfigureAwait()
//
// then we can figure out what 'foo' should be based on teh await
// context.
var name = memberAccessExpression.Name.Identifier.Value;
if (name.Equals(nameof(Task<int>.ConfigureAwait)) &&
memberAccessExpression.IsParentKind(SyntaxKind.InvocationExpression) &&
memberAccessExpression.Parent.IsParentKind(SyntaxKind.AwaitExpression))
{
return InferTypes((ExpressionSyntax)memberAccessExpression.Parent);
}
else if (name.Equals(nameof(Task<int>.ContinueWith)))
{
// foo.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.
var taskOfT = this.Compilation.TaskOfTType();
if (taskOfT != null)
{
return SpecializedCollections.SingletonEnumerable(
taskOfT.Construct(this.Compilation.ObjectType));
}
}
else if (name.Equals(nameof(Enumerable.Select)) ||
name.Equals(nameof(Enumerable.Where)))
{
var ienumerableType = this.Compilation.IEnumerableOfTType();
// foo.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 != null && memberAccessExpression.IsParentKind(SyntaxKind.InvocationExpression))
{
var invocation = (InvocationExpressionSyntax)memberAccessExpression.Parent;
if (invocation.ArgumentList.Arguments.Count > 0)
{
// foo.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.
var taskOfT = this.Compilation.TaskOfTType();
if (taskOfT != null)
var argumentExpression = invocation.ArgumentList.Arguments[0].Expression;
var argumentTypes = GetTypes(argumentExpression);
var delegateType = argumentTypes.FirstOrDefault().GetDelegateType(this.Compilation);
var typeArg = delegateType?.TypeArguments.Length > 0
? delegateType.TypeArguments[0]
: this.Compilation.ObjectType;
if (IsUnusableType(typeArg) && argumentExpression is LambdaExpressionSyntax)
{
return SpecializedCollections.SingletonEnumerable(
taskOfT.Construct(this.Compilation.ObjectType));
typeArg = InferTypeForFirstParameterOfLambda((LambdaExpressionSyntax)argumentExpression) ??
this.Compilation.ObjectType;
}
}
else if (name.Equals(nameof(Enumerable.Select)) ||
name.Equals(nameof(Enumerable.Where)))
{
var ienumerableType = this.Compilation.IEnumerableOfTType();
// foo.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 != null && memberAccessExpression.IsParentKind(SyntaxKind.InvocationExpression))
{
var invocation = (InvocationExpressionSyntax)memberAccessExpression.Parent;
if (invocation.ArgumentList.Arguments.Count > 0)
{
var argumentExpression = invocation.ArgumentList.Arguments[0].Expression;
var argumentTypes = GetTypes(argumentExpression);
var delegateType = argumentTypes.FirstOrDefault().GetDelegateType(this.Compilation);
var typeArg = delegateType?.TypeArguments.Length > 0
? delegateType.TypeArguments[0]
: this.Compilation.ObjectType;
if (IsUnusableType(typeArg) && argumentExpression is LambdaExpressionSyntax)
{
typeArg = InferTypeForFirstParameterOfLambda((LambdaExpressionSyntax)argumentExpression) ??
this.Compilation.ObjectType;
}
return SpecializedCollections.SingletonEnumerable(
ienumerableType.Construct(typeArg));
}
}
return SpecializedCollections.SingletonEnumerable(
ienumerableType.Construct(typeArg));
}
return SpecializedCollections.EmptyEnumerable<ITypeSymbol>();
}
// We're right after the dot in "Foo.Bar". The type for "Bar" should be
// whatever type we'd infer for "Foo.Bar" itself.
return InferTypes(memberAccessExpression);
}
return SpecializedCollections.EmptyEnumerable<ITypeSymbol>();
}
private ITypeSymbol InferTypeForFirstParameterOfLambda(
......
......@@ -828,17 +828,107 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
' then we can figure out what 'foo' should be based on teh await
' context.
If expressionOpt Is memberAccessExpression.Expression Then
If memberAccessExpression.Name.Identifier.Value.Equals(NameOf(Task(Of Integer).ConfigureAwait)) AndAlso
memberAccessExpression.IsParentKind(SyntaxKind.InvocationExpression) AndAlso
memberAccessExpression.Parent.IsParentKind(SyntaxKind.AwaitExpression) Then
Return InferTypes(DirectCast(memberAccessExpression.Parent, ExpressionSyntax))
Return InferTypeForExpressionOfMemberAccessExpression(memberAccessExpression)
End If
Return InferTypes(memberAccessExpression)
End If
End Function
Private Function InferTypeForExpressionOfMemberAccessExpression(memberAccessExpression As MemberAccessExpressionSyntax) As IEnumerable(Of ITypeSymbol)
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
' foo.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 SpecializedCollections.SingletonEnumerable(
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()
' foo.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.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().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 SpecializedCollections.SingletonEnumerable(
ienumerableType.Construct(typeArg))
End If
End If
End If
Return SpecializedCollections.EmptyEnumerable(Of ITypeSymbol)()
Return SpecializedCollections.EmptyEnumerable(Of ITypeSymbol)()
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 InferTypes(memberAccessExpression)
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) Then
Return InferTypes(identifier).FirstOrDefault()
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, Optional previousToken As SyntaxToken = Nothing) As IEnumerable(Of ITypeSymbol)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册