提交 7abdbb53 编写于 作者: G gafter

Improve lambda type inference for erroneous method groups

We attempt to bind lambda args against every applicable delegate
type that could be a matching parameter, and then select the
"best" for error recovery.
This also has the effect of removing unbound lambdas from the
bound trees, replacing them with the "best" binding for error
recovery. This makes the semantic model insensitive to any further
trial bindings (i.e. mutation of the unbound lambda cache).
Fixes #12063, #11979, #11901

Remove vestigal "extensionMethodsOfSameViabilityAreAvailable"
thereby reducing coupling between internal compiler APIs.
See also #7740 #5128, where the vestigal APIs were introduced.
上级 ea2bca85
......@@ -3115,8 +3115,7 @@ private static bool IsNegativeConstantForArraySize(BoundExpression expression)
typeArguments: ImmutableArray<TypeSymbol>.Empty,
analyzedArguments: analyzedArguments,
invokedAsExtensionMethod: false,
isDelegate: false,
extensionMethodsOfSameViabilityAreAvailable: false);
isDelegate: false);
result.WasCompilerGenerated = initializerArgumentListOpt == null;
return result;
}
......@@ -4221,17 +4220,7 @@ private bool IsConstructorAccessible(MethodSymbol constructor, ref HashSet<Diagn
// NOTE: The use site diagnostics of the candidate constructors have already been reported (in PerformConstructorOverloadResolution).
ArrayBuilder<BoundNode> childNodes = ArrayBuilder<BoundNode>.GetInstance();
if (candidateConstructors.Length == 1)
{
ImmutableArray<BoundExpression> args = BuildArgumentsForErrorRecovery(analyzedArguments, candidateConstructors[0].Parameters);
childNodes.AddRange(args);
}
else
{
childNodes.AddRange(BuildArgumentsForErrorRecovery(analyzedArguments));
}
childNodes.AddRange(BuildArgumentsForErrorRecovery(analyzedArguments, candidateConstructors));
if (boundInitializerOpt != null)
{
childNodes.Add(boundInitializerOpt);
......@@ -5537,8 +5526,7 @@ private static void CombineExtensionMethodArguments(BoundExpression receiver, An
OverloadResolution.MethodInvocationOverloadResolution(methodGroup.Methods, methodGroup.TypeArguments, actualArguments, overloadResolutionResult, ref useSiteDiagnostics, isMethodGroupConversion, allowRefOmittedArguments);
diagnostics.Add(expression, useSiteDiagnostics);
var sealedDiagnostics = diagnostics.ToReadOnlyAndFree();
var result = new MethodGroupResolution(methodGroup, null, overloadResolutionResult, actualArguments, methodGroup.ResultKind, sealedDiagnostics,
extensionMethodsOfSameViabilityAreAvailable: false);
var result = new MethodGroupResolution(methodGroup, null, overloadResolutionResult, actualArguments, methodGroup.ResultKind, sealedDiagnostics);
// If the search in the current scope resulted in any applicable method (regardless of whether a best
// applicable method could be determined) then our search is complete. Otherwise, store aside the
......@@ -6430,20 +6418,11 @@ private BoundExpression BindIndexedPropertyAccess(CSharpSyntaxNode syntax, Bound
delegateTypeBeingInvoked: null);
}
PropertySymbol property;
ImmutableArray<BoundExpression> arguments;
if (candidates.Length == 1)
{
property = candidates[0];
arguments = BuildArgumentsForErrorRecovery(analyzedArguments, property.Parameters);
}
else
{
// A bad BoundIndexerAccess containing an ErrorPropertySymbol will produce better flow analysis results than
// a BoundBadExpression containing the candidate indexers.
property = CreateErrorPropertySymbol(candidates);
arguments = BuildArgumentsForErrorRecovery(analyzedArguments);
}
ImmutableArray<BoundExpression> arguments = BuildArgumentsForErrorRecovery(analyzedArguments, candidates);
// A bad BoundIndexerAccess containing an ErrorPropertySymbol will produce better flow analysis results than
// a BoundBadExpression containing the candidate indexers.
PropertySymbol property = (candidates.Length == 1) ? candidates[0] : CreateErrorPropertySymbol(candidates);
propertyAccess = BoundIndexerAccess.ErrorAccess(
syntax,
......@@ -6537,8 +6516,7 @@ private ErrorPropertySymbol CreateErrorPropertySymbol(ImmutableArray<PropertySym
var diagnostics = DiagnosticBag.GetInstance();
diagnostics.AddRange(methodResolution.Diagnostics); // Could still have use site warnings.
BindMemberAccessReportError(node, diagnostics);
return new MethodGroupResolution(methodResolution.MethodGroup, methodResolution.OtherSymbol, methodResolution.OverloadResolutionResult, methodResolution.AnalyzedArguments, methodResolution.ResultKind, diagnostics.ToReadOnlyAndFree(),
methodResolution.ExtensionMethodsOfSameViabilityAreAvailable);
return new MethodGroupResolution(methodResolution.MethodGroup, methodResolution.OtherSymbol, methodResolution.OverloadResolutionResult, methodResolution.AnalyzedArguments, methodResolution.ResultKind, diagnostics.ToReadOnlyAndFree());
}
return methodResolution;
}
......@@ -6565,9 +6543,7 @@ private ErrorPropertySymbol CreateErrorPropertySymbol(ImmutableArray<PropertySym
}
var extensionMethodResolution = BindExtensionMethod(expression, methodName, analyzedArguments, methodGroup.ReceiverOpt, methodGroup.TypeArgumentsOpt, isMethodGroupConversion);
bool preferExtensionMethodResolution = false;
bool extensionMethodsOfSameViabilityAreAvailable = false;
if (extensionMethodResolution.HasAnyApplicableMethod)
{
......@@ -6593,12 +6569,8 @@ private ErrorPropertySymbol CreateErrorPropertySymbol(ImmutableArray<PropertySym
LookupResultKind methodResultKind = methodResolution.ResultKind;
LookupResultKind extensionMethodResultKind = extensionMethodResolution.ResultKind;
if (methodResultKind == extensionMethodResultKind)
{
// This information allows us to preserve more useful information for SemanticModel.
extensionMethodsOfSameViabilityAreAvailable = true;
}
else if (methodResultKind == extensionMethodResultKind.WorseResultKind(methodResultKind))
if (methodResultKind != extensionMethodResultKind &&
methodResultKind == extensionMethodResultKind.WorseResultKind(methodResultKind))
{
preferExtensionMethodResolution = true;
}
......@@ -6613,17 +6585,6 @@ private ErrorPropertySymbol CreateErrorPropertySymbol(ImmutableArray<PropertySym
extensionMethodResolution.Free();
if (extensionMethodsOfSameViabilityAreAvailable)
{
return new MethodGroupResolution(methodResolution.MethodGroup,
methodResolution.OtherSymbol,
methodResolution.OverloadResolutionResult,
methodResolution.AnalyzedArguments,
methodResolution.ResultKind,
methodResolution.Diagnostics,
extensionMethodsOfSameViabilityAreAvailable: true);
}
return methodResolution;
}
......@@ -6682,8 +6643,7 @@ private ErrorPropertySymbol CreateErrorPropertySymbol(ImmutableArray<PropertySym
methodGroup.Methods, methodGroup.TypeArguments, analyzedArguments,
result, ref useSiteDiagnostics, isMethodGroupConversion, allowRefOmittedArguments,
inferWithDynamic: inferWithDynamic, allowUnexpandedForm: allowUnexpandedForm);
return new MethodGroupResolution(methodGroup, null, result, analyzedArguments, methodGroup.ResultKind, sealedDiagnostics,
extensionMethodsOfSameViabilityAreAvailable: false);
return new MethodGroupResolution(methodGroup, null, result, analyzedArguments, methodGroup.ResultKind, sealedDiagnostics);
}
}
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
......@@ -414,7 +415,7 @@ private static ImmutableArray<BoundExpression> BuildArgumentsForDynamicInvocatio
}
else
{
result = BindInvocationExpressionContinued(node, expression, methodName, overloadResolutionResult, analyzedArguments, methodGroup, delegateType, diagnostics, false, queryClause);
result = BindInvocationExpressionContinued(node, expression, methodName, overloadResolutionResult, analyzedArguments, methodGroup, delegateType, diagnostics, queryClause);
}
overloadResolutionResult.Free();
......@@ -482,8 +483,7 @@ private static bool HasApplicableConditionalMethod(OverloadResolutionResult<Meth
typeArguments,
analyzedArguments,
invokedAsExtensionMethod: resolution.IsExtensionMethodGroup,
isDelegate: false,
extensionMethodsOfSameViabilityAreAvailable: resolution.ExtensionMethodsOfSameViabilityAreAvailable);
isDelegate: false);
}
else if (!resolution.IsEmpty)
{
......@@ -498,8 +498,9 @@ private static bool HasApplicableConditionalMethod(OverloadResolutionResult<Meth
{
// we want to force any unbound lambda arguments to cache an appropriate conversion if possible; see 9448.
DiagnosticBag discarded = DiagnosticBag.GetInstance();
result = BindInvocationExpressionContinued(syntax, expression, methodName, resolution.OverloadResolutionResult, resolution.AnalyzedArguments, resolution.MethodGroup, null, discarded,
resolution.ExtensionMethodsOfSameViabilityAreAvailable, queryClause);
result = BindInvocationExpressionContinued(
syntax, expression, methodName, resolution.OverloadResolutionResult, resolution.AnalyzedArguments,
resolution.MethodGroup, delegateTypeOpt: null, diagnostics: discarded, queryClause: queryClause);
discarded.Free();
}
......@@ -564,7 +565,7 @@ private static bool HasApplicableConditionalMethod(OverloadResolutionResult<Meth
{
result = BindInvocationExpressionContinued(
syntax, expression, methodName, resolution.OverloadResolutionResult, resolution.AnalyzedArguments,
resolution.MethodGroup, null, diagnostics, resolution.ExtensionMethodsOfSameViabilityAreAvailable, queryClause);
resolution.MethodGroup, delegateTypeOpt: null, diagnostics: diagnostics, queryClause: queryClause);
}
}
}
......@@ -647,7 +648,6 @@ private static bool HasApplicableConditionalMethod(OverloadResolutionResult<Meth
methodGroup: resolution.MethodGroup,
delegateTypeOpt: null,
diagnostics: diagnostics,
extensionMethodsOfSameViabilityAreAvailable: resolution.ExtensionMethodsOfSameViabilityAreAvailable,
queryClause: queryClause);
}
......@@ -767,10 +767,6 @@ private static void CheckRestrictedTypeReceiver(BoundExpression expression, Comp
/// <param name="methodGroup">Method group if the invocation represents a potentially overloaded member.</param>
/// <param name="delegateTypeOpt">Delegate type if method group represents a delegate.</param>
/// <param name="diagnostics">Diagnostics.</param>
/// <param name="extensionMethodsOfSameViabilityAreAvailable">
/// Indicates that there are additional extension method candidates of the same lookup viability in cases when
/// none of the instance methods are applicable to the argument list.
/// </param>
/// <param name="queryClause">The syntax for the query clause generating this invocation expression, if any.</param>
/// <returns>BoundCall or error expression representing the invocation.</returns>
private BoundCall BindInvocationExpressionContinued(
......@@ -782,7 +778,6 @@ private static void CheckRestrictedTypeReceiver(BoundExpression expression, Comp
MethodGroup methodGroup,
NamedTypeSymbol delegateTypeOpt,
DiagnosticBag diagnostics,
bool extensionMethodsOfSameViabilityAreAvailable,
CSharpSyntaxNode queryClause = null)
{
Debug.Assert(node != null);
......@@ -834,8 +829,7 @@ private static void CheckRestrictedTypeReceiver(BoundExpression expression, Comp
}
return CreateBadCall(node, methodGroup.Name, invokedAsExtensionMethod && analyzedArguments.Arguments.Count > 0 && (object)methodGroup.Receiver == (object)analyzedArguments.Arguments[0] ? null : methodGroup.Receiver,
GetOriginalMethods(result), methodGroup.ResultKind, methodGroup.TypeArguments.ToImmutable(), analyzedArguments, invokedAsExtensionMethod: invokedAsExtensionMethod, isDelegate: ((object)delegateTypeOpt != null),
extensionMethodsOfSameViabilityAreAvailable: extensionMethodsOfSameViabilityAreAvailable);
GetOriginalMethods(result), methodGroup.ResultKind, methodGroup.TypeArguments.ToImmutable(), analyzedArguments, invokedAsExtensionMethod: invokedAsExtensionMethod, isDelegate: ((object)delegateTypeOpt != null));
}
// Otherwise, there were no dynamic arguments and overload resolution found a unique best candidate.
......@@ -1059,8 +1053,7 @@ private static NamedTypeSymbol GetDelegateType(BoundExpression expr)
ImmutableArray<TypeSymbol> typeArguments,
AnalyzedArguments analyzedArguments,
bool invokedAsExtensionMethod,
bool isDelegate,
bool extensionMethodsOfSameViabilityAreAvailable)
bool isDelegate)
{
MethodSymbol method;
ImmutableArray<BoundExpression> args;
......@@ -1075,13 +1068,9 @@ private static NamedTypeSymbol GetDelegateType(BoundExpression expr)
methods = constructedMethods.ToImmutableAndFree();
}
if (methods.Length == 1 && !extensionMethodsOfSameViabilityAreAvailable)
if (methods.Length == 1 && !IsUnboundGeneric(methods[0]))
{
// If there is only one method in the group and no additional extension methods with the same lookup viability,
// we should attempt to bind to it. That includes binding any lambdas in the argument list against
// the method's parameter types.
method = methods[0];
args = BuildArgumentsForErrorRecovery(analyzedArguments, method.Parameters);
}
else
{
......@@ -1090,61 +1079,113 @@ private static NamedTypeSymbol GetDelegateType(BoundExpression expr)
? receiver.Type
: this.ContainingType;
method = new ErrorMethodSymbol(methodContainer, returnType, name);
args = BuildArgumentsForErrorRecovery(analyzedArguments);
}
args = BuildArgumentsForErrorRecovery(analyzedArguments, methods);
var argNames = analyzedArguments.GetNames();
var argRefKinds = analyzedArguments.RefKinds.ToImmutableOrNull();
return BoundCall.ErrorCall(node, receiver, method, args, argNames, argRefKinds, isDelegate, invokedAsExtensionMethod: invokedAsExtensionMethod, originalMethods: methods, resultKind: resultKind);
}
private static ImmutableArray<BoundExpression> BuildArgumentsForErrorRecovery(AnalyzedArguments analyzedArguments, ImmutableArray<ParameterSymbol> parameters)
private static bool IsUnboundGeneric(MethodSymbol method)
{
ArrayBuilder<BoundExpression> oldArguments = analyzedArguments.Arguments;
int argumentCount = oldArguments.Count;
int parameterCount = parameters.Length;
return method.IsGenericMethod && method.ConstructedFrom() == method;
}
for (int i = 0; i < argumentCount; i++)
private static ImmutableArray<BoundExpression> BuildArgumentsForErrorRecovery(AnalyzedArguments analyzedArguments, ImmutableArray<MethodSymbol> methods)
{
var parameterListList = ArrayBuilder<ImmutableArray<ParameterSymbol>>.GetInstance();
foreach (var m in methods)
{
BoundKind argumentKind = oldArguments[i].Kind;
if (!IsUnboundGeneric(m) && m.ParameterCount > 0) parameterListList.Add(m.Parameters);
}
if (argumentKind == BoundKind.UnboundLambda && i < parameterCount)
{
ArrayBuilder<BoundExpression> newArguments = ArrayBuilder<BoundExpression>.GetInstance(argumentCount);
newArguments.AddRange(oldArguments);
var result = BuildArgumentsForErrorRecovery(analyzedArguments, parameterListList);
parameterListList.Free();
return result;
}
do
{
BoundExpression oldArgument = newArguments[i];
private static ImmutableArray<BoundExpression> BuildArgumentsForErrorRecovery(AnalyzedArguments analyzedArguments, ImmutableArray<PropertySymbol> properties)
{
var parameterListList = ArrayBuilder<ImmutableArray<ParameterSymbol>>.GetInstance();
foreach (var m in properties)
{
if (m.ParameterCount > 0) parameterListList.Add(m.Parameters);
}
var result = BuildArgumentsForErrorRecovery(analyzedArguments, parameterListList);
parameterListList.Free();
return result;
}
if (i < parameterCount)
private static ImmutableArray<BoundExpression> BuildArgumentsForErrorRecovery(AnalyzedArguments analyzedArguments, IEnumerable<ImmutableArray<ParameterSymbol>> parameterListList)
{
// Since the purpose is to bind any unbound lambdas, we return early if there are none.
if (!analyzedArguments.Arguments.Any(e => e.Kind == BoundKind.UnboundLambda))
{
return analyzedArguments.Arguments.ToImmutable();
}
int argumentCount = analyzedArguments.Arguments.Count;
ArrayBuilder<BoundExpression> newArguments = ArrayBuilder<BoundExpression>.GetInstance(argumentCount);
newArguments.AddRange(analyzedArguments.Arguments);
for (int i = 0; i < argumentCount; i++)
{
var argument = newArguments[i];
if (argument.Kind == BoundKind.UnboundLambda)
{
// bind the argument against each applicable parameter
var unboundArgument = (UnboundLambda)argument;
foreach (var parameterList in parameterListList)
{
var parameterType = GetCorrespondingParameterType(analyzedArguments, i, parameterList);
if (parameterType?.Kind == SymbolKind.NamedType)
{
switch (oldArgument.Kind)
{
case BoundKind.UnboundLambda:
NamedTypeSymbol parameterType = parameters[i].Type as NamedTypeSymbol;
if ((object)parameterType != null)
{
newArguments[i] = ((UnboundLambda)oldArgument).Bind(parameterType);
}
break;
}
var discarded = unboundArgument.Bind((NamedTypeSymbol)parameterType);
}
i++;
}
while (i < argumentCount);
return newArguments.ToImmutableAndFree();
// replace the unbound lambda with its best inferred bound version
newArguments[i] = unboundArgument.BindForErrorRecovery();
}
}
return newArguments.ToImmutableAndFree();
}
/// <summary>
/// Compute the type of the corresponding parameter, if any. This is used to improve error recovery,
/// for bad invocations, not for semantic analysis of correct invocations, so it is a heuristic.
/// If no parameter appears to correspond to the given argument, we return null.
/// </summary>
/// <param name="analyzedArguments">The analyzed argument list</param>
/// <param name="i">The index of the argument</param>
/// <param name="parameterList">The parameter list to match against</param>
/// <returns>The type of the corresponding parameter.</returns>
private static TypeSymbol GetCorrespondingParameterType(AnalyzedArguments analyzedArguments, int i, ImmutableArray<ParameterSymbol> parameterList)
{
string name = analyzedArguments.Name(i);
if (name != null)
{
// look for a parameter by that name
foreach (var parameter in parameterList)
{
if (parameter.Name == name) return parameter.Type;
}
return null;
}
return oldArguments.ToImmutable();
return (i < parameterList.Length) ? parameterList[i].Type : null;
// CONSIDER: should we handle variable argument lists?
}
/// <summary>
/// Absent parameter types to bind the arguments, we simply use the arguments provided for error recovery.
/// </summary>
private static ImmutableArray<BoundExpression> BuildArgumentsForErrorRecovery(AnalyzedArguments analyzedArguments)
{
return BuildArgumentsForErrorRecovery(analyzedArguments, ImmutableArray<ParameterSymbol>.Empty);
return BuildArgumentsForErrorRecovery(analyzedArguments, Enumerable.Empty<ImmutableArray<ParameterSymbol>>());
}
private BoundCall CreateBadCall(
......
......@@ -19,10 +19,9 @@ internal struct MethodGroupResolution
public readonly AnalyzedArguments AnalyzedArguments;
public readonly ImmutableArray<Diagnostic> Diagnostics;
public readonly LookupResultKind ResultKind;
public readonly bool ExtensionMethodsOfSameViabilityAreAvailable;
public MethodGroupResolution(MethodGroup methodGroup, ImmutableArray<Diagnostic> diagnostics)
: this(methodGroup, null, null, null, methodGroup.ResultKind, diagnostics, false)
: this(methodGroup, null, null, null, methodGroup.ResultKind, diagnostics)
{
}
......@@ -31,12 +30,12 @@ public MethodGroupResolution(MethodGroup methodGroup, ImmutableArray<Diagnostic>
OverloadResolutionResult<MethodSymbol> overloadResolutionResult,
AnalyzedArguments analyzedArguments,
ImmutableArray<Diagnostic> diagnostics)
: this(methodGroup, null, overloadResolutionResult, analyzedArguments, methodGroup.ResultKind, diagnostics, false)
: this(methodGroup, null, overloadResolutionResult, analyzedArguments, methodGroup.ResultKind, diagnostics)
{
}
public MethodGroupResolution(Symbol otherSymbol, LookupResultKind resultKind, ImmutableArray<Diagnostic> diagnostics)
: this(null, otherSymbol, null, null, resultKind, diagnostics, false)
: this(null, otherSymbol, null, null, resultKind, diagnostics)
{
}
......@@ -46,8 +45,7 @@ public MethodGroupResolution(Symbol otherSymbol, LookupResultKind resultKind, Im
OverloadResolutionResult<MethodSymbol> overloadResolutionResult,
AnalyzedArguments analyzedArguments,
LookupResultKind resultKind,
ImmutableArray<Diagnostic> diagnostics,
bool extensionMethodsOfSameViabilityAreAvailable)
ImmutableArray<Diagnostic> diagnostics)
{
Debug.Assert((methodGroup == null) || (methodGroup.Methods.Count > 0));
Debug.Assert((methodGroup == null) || ((object)otherSymbol == null));
......@@ -55,7 +53,6 @@ public MethodGroupResolution(Symbol otherSymbol, LookupResultKind resultKind, Im
Debug.Assert(((object)otherSymbol == null) || (otherSymbol.Kind != SymbolKind.Method));
Debug.Assert(resultKind != LookupResultKind.Ambiguous); // HasAnyApplicableMethod is expecting Viable methods.
Debug.Assert(!diagnostics.IsDefault);
Debug.Assert(!extensionMethodsOfSameViabilityAreAvailable || methodGroup == null || !methodGroup.IsExtensionMethodGroup);
this.MethodGroup = methodGroup;
this.OtherSymbol = otherSymbol;
......@@ -63,7 +60,6 @@ public MethodGroupResolution(Symbol otherSymbol, LookupResultKind resultKind, Im
this.AnalyzedArguments = analyzedArguments;
this.ResultKind = resultKind;
this.Diagnostics = diagnostics;
this.ExtensionMethodsOfSameViabilityAreAvailable = extensionMethodsOfSameViabilityAreAvailable;
}
public bool IsEmpty
......
......@@ -2062,6 +2062,201 @@ public static class XThing
public static void X1(this object self) {}
public static void X2(this object self) {}
}
";
var compilation = CreateCompilationWithMscorlibAndSystemCore(source);
var tree = compilation.SyntaxTrees[0];
var sm = compilation.GetSemanticModel(tree);
foreach (var lambda in tree.GetRoot().DescendantNodes().OfType<LambdaExpressionSyntax>())
{
var reference = lambda.Body.DescendantNodesAndSelf().OfType<IdentifierNameSyntax>().First();
Assert.Equal("x", reference.ToString());
var typeInfo = sm.GetTypeInfo(reference);
Assert.Equal(TypeKind.Class, typeInfo.Type.TypeKind);
Assert.Equal("String", typeInfo.Type.Name);
Assert.NotEmpty(typeInfo.Type.GetMembers("Replace"));
}
}
[Fact]
[WorkItem(11901, "https://github.com/dotnet/roslyn/issues/11901")]
public void TestLambdaWithError15()
{
// These tests ensure we attempt to perform type inference and bind a lambda expression
// argument even when there are too many or too few arguments to an invocation, in the
// case when there is exactly one method in the method group.
var source =
@"using System;
class Program
{
static void Main(string[] args)
{
Thing<string> t = null;
t.X1(x => x, 1); // too many args
t.X2(x => x); // too few args
t.M2(string.Empty, x => x, 1); // too many args
t.M3(string.Empty, x => x); // too few args
}
}
public class Thing<T>
{
public void M2<T>(T x, Func<T, T> func) {}
public void M3<T>(T x, Func<T, T> func, T y) {}
}
public static class XThing
{
public static Thing<T> X1<T>(this Thing<T> self, Func<T, T> func) => null;
public static Thing<T> X2<T>(this Thing<T> self, Func<T, T> func, int i) => null;
}
";
var compilation = CreateCompilationWithMscorlibAndSystemCore(source);
var tree = compilation.SyntaxTrees[0];
var sm = compilation.GetSemanticModel(tree);
foreach (var lambda in tree.GetRoot().DescendantNodes().OfType<LambdaExpressionSyntax>())
{
var reference = lambda.Body.DescendantNodesAndSelf().OfType<IdentifierNameSyntax>().First();
Assert.Equal("x", reference.ToString());
var typeInfo = sm.GetTypeInfo(reference);
Assert.Equal(TypeKind.Class, typeInfo.Type.TypeKind);
Assert.Equal("String", typeInfo.Type.Name);
Assert.NotEmpty(typeInfo.Type.GetMembers("Replace"));
}
}
[Fact]
[WorkItem(11901, "https://github.com/dotnet/roslyn/issues/11901")]
public void TestLambdaWithError16()
{
// These tests ensure we use the substituted method to bind a lambda expression
// argument even when there are too many or too few arguments to an invocation, in the
// case when there is exactly one method in the method group.
var source =
@"using System;
class Program
{
static void Main(string[] args)
{
Thing<string> t = null;
t.X1<string>(x => x, 1); // too many args
t.X2<string>(x => x); // too few args
t.M2<string>(string.Empty, x => x, 1); // too many args
t.M3<string>(string.Empty, x => x); // too few args
}
}
public class Thing<T>
{
public void M2<T>(T x, Func<T, T> func) {}
public void M3<T>(T x, Func<T, T> func, T y) {}
}
public static class XThing
{
public static Thing<T> X1<T>(this Thing<T> self, Func<T, T> func) => null;
public static Thing<T> X2<T>(this Thing<T> self, Func<T, T> func, int i) => null;
}
";
var compilation = CreateCompilationWithMscorlibAndSystemCore(source);
var tree = compilation.SyntaxTrees[0];
var sm = compilation.GetSemanticModel(tree);
foreach (var lambda in tree.GetRoot().DescendantNodes().OfType<LambdaExpressionSyntax>())
{
var reference = lambda.Body.DescendantNodesAndSelf().OfType<IdentifierNameSyntax>().First();
Assert.Equal("x", reference.ToString());
var typeInfo = sm.GetTypeInfo(reference);
Assert.Equal(TypeKind.Class, typeInfo.Type.TypeKind);
Assert.Equal("String", typeInfo.Type.Name);
Assert.NotEmpty(typeInfo.Type.GetMembers("Replace"));
}
}
[Fact]
[WorkItem(12063, "https://github.com/dotnet/roslyn/issues/12063")]
public void TestLambdaWithError17()
{
var source =
@"using System;
class Program
{
static void Main(string[] args)
{
Ma(action: (x, y) => x.ToString(), t: string.Empty);
Mb(action: (x, y) => x.ToString(), t: string.Empty);
}
static void Ma<T>(T t, Action<T, T, int> action) { }
static void Mb<T>(T t, Action<T, T, int> action) { }
static void Mb() { }
}
";
var compilation = CreateCompilationWithMscorlibAndSystemCore(source);
var tree = compilation.SyntaxTrees[0];
var sm = compilation.GetSemanticModel(tree);
foreach (var lambda in tree.GetRoot().DescendantNodes().OfType<LambdaExpressionSyntax>())
{
var reference = lambda.Body.DescendantNodesAndSelf().OfType<IdentifierNameSyntax>().First();
Assert.Equal("x", reference.ToString());
var typeInfo = sm.GetTypeInfo(reference);
Assert.Equal(TypeKind.Class, typeInfo.Type.TypeKind);
Assert.Equal("String", typeInfo.Type.Name);
Assert.NotEmpty(typeInfo.Type.GetMembers("Replace"));
}
}
[Fact]
[WorkItem(12063, "https://github.com/dotnet/roslyn/issues/12063")]
public void TestLambdaWithError18()
{
var source =
@"using System;
class Program
{
static void Main(string[] args)
{
Ma(string.Empty, (x, y) => x.ToString());
Mb(string.Empty, (x, y) => x.ToString());
}
static void Ma<T>(T t, Action<T, T, int> action) { }
static void Mb<T>(T t, Action<T, T, int> action) { }
static void Mb() { }
}
";
var compilation = CreateCompilationWithMscorlibAndSystemCore(source);
var tree = compilation.SyntaxTrees[0];
var sm = compilation.GetSemanticModel(tree);
foreach (var lambda in tree.GetRoot().DescendantNodes().OfType<LambdaExpressionSyntax>())
{
var reference = lambda.Body.DescendantNodesAndSelf().OfType<IdentifierNameSyntax>().First();
Assert.Equal("x", reference.ToString());
var typeInfo = sm.GetTypeInfo(reference);
Assert.Equal(TypeKind.Class, typeInfo.Type.TypeKind);
Assert.Equal("String", typeInfo.Type.Name);
Assert.NotEmpty(typeInfo.Type.GetMembers("Replace"));
}
}
[Fact]
[WorkItem(12063, "https://github.com/dotnet/roslyn/issues/12063")]
public void TestLambdaWithError19()
{
var source =
@"using System;
using System.Linq.Expressions;
class Program
{
static void Main(string[] args)
{
Ma(string.Empty, (x, y) => x.ToString());
Mb(string.Empty, (x, y) => x.ToString());
Mc(string.Empty, (x, y) => x.ToString());
}
static void Ma<T>(T t, Expression<Action<T, T, int>> action) { }
static void Mb<T>(T t, Expression<Action<T, T, int>> action) { }
static void Mb<T>(T t, Action<T, T, int> action) { }
static void Mc<T>(T t, Expression<Action<T, T, int>> action) { }
static void Mc() { }
}
";
var compilation = CreateCompilationWithMscorlibAndSystemCore(source);
var tree = compilation.SyntaxTrees[0];
......
......@@ -1997,6 +1997,9 @@ void Test11()
// (115,15): error CS0103: The name 'u9' does not exist in the current context
// Dummy(u9);
Diagnostic(ErrorCode.ERR_NameNotInContext, "u9").WithArguments("u9").WithLocation(115, 15),
// (108,50): error CS0165: Use of unassigned local variable 'z9'
// group x > y9 && 1 is var z9 && z9 ==
Diagnostic(ErrorCode.ERR_UseDefViolation, "z9").WithArguments("z9").WithLocation(108, 50),
// (121,24): error CS1931: The range variable 'y10' conflicts with a previous declaration of 'y10'
// from y10 in new[] { 1 }
Diagnostic(ErrorCode.ERR_QueryRangeVariableOverrides, "y10").WithArguments("y10").WithLocation(121, 24),
......
......@@ -254,6 +254,46 @@ class C
Mumble(new { x = 42 }, (a, y) => a.x);
}
}
</code>
Await TestAsync(input, expected, expandParameter:=True)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.Expansion)>
<WorkItem(11979, "https://github.com/dotnet/roslyn/issues/11979")>
Public Async Function TestCSharp_LambdaParameter_DontExpandAnonymousTypes2_variation() As Task
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document>
using System;
class C
{
static void Mumble&lt;T&gt;(T anonymousType, Action&lt;T, int, int&gt; lambda) { }
static void Mumble() { } // added to the test
static void M()
{
Mumble(new { x = 42 }, {|Expand:(a, y) => a.x|});
}
}
</Document>
</Project>
</Workspace>
Dim expected =
<code>
using System;
class C
{
static void Mumble&lt;T&gt;(T anonymousType, Action&lt;T, int, int&gt; lambda) { }
static void Mumble() { } // added to the test
static void M()
{
Mumble(new { x = 42 }, (a, y) => a.x);
}
}
</code>
Await TestAsync(input, expected, expandParameter:=True)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册