Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Attempt type inference when there are no applicable methods #11718

Merged
merged 1 commit into from
Jun 3, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 0 additions & 37 deletions src/Compilers/CSharp/Portable/Binder/Binder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -605,43 +605,6 @@ private static ThreeState ReportDiagnosticsIfObsoleteInternal(DiagnosticBag diag
return ThreeState.True;
}

internal void ResolveOverloads<TMember>(
ImmutableArray<TMember> members,
ImmutableArray<TypeSymbol> typeArguments,
ImmutableArray<ArgumentSyntax> arguments,
OverloadResolutionResult<TMember> result,
ref HashSet<DiagnosticInfo> useSiteDiagnostics,
bool allowRefOmittedArguments)
where TMember : Symbol
{
var methodsBuilder = ArrayBuilder<TMember>.GetInstance(members.Length);
methodsBuilder.AddRange(members);

var typeArgumentsBuilder = ArrayBuilder<TypeSymbol>.GetInstance(typeArguments.Length);
typeArgumentsBuilder.AddRange(typeArguments);

var analyzedArguments = AnalyzedArguments.GetInstance();
var unusedDiagnostics = DiagnosticBag.GetInstance();
foreach (var argumentSyntax in arguments)
{
BindArgumentAndName(analyzedArguments, unusedDiagnostics, false, argumentSyntax, allowArglist: false);
}

OverloadResolution.MethodOrPropertyOverloadResolution(
methodsBuilder,
typeArgumentsBuilder,
analyzedArguments,
result,
isMethodGroupConversion: false,
allowRefOmittedArguments: allowRefOmittedArguments,
useSiteDiagnostics: ref useSiteDiagnostics);

methodsBuilder.Free();
typeArgumentsBuilder.Free();
analyzedArguments.Free();
unusedDiagnostics.Free();
}

internal bool IsSymbolAccessibleConditional(
Symbol symbol,
AssemblySymbol within,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,17 +252,16 @@ private void PerformMemberOverloadResolution<TMember>(
// Also note that less derived members are not actually removed - they are simply flagged.
ReportUseSiteDiagnostics(results, ref useSiteDiagnostics);

// SPEC: If the resulting set of candidate methods is empty, then further processing along the following steps are abandoned,
// SPEC: and instead an attempt is made to process the invocation as an extension method invocation. If this fails, then no
// SPEC: applicable methods exist, and a binding-time error occurs.
// SPEC: If the resulting set of candidate methods is empty, then further processing along the following steps are abandoned,
// SPEC: and instead an attempt is made to process the invocation as an extension method invocation. If this fails, then no
// SPEC: applicable methods exist, and a binding-time error occurs.
if (RemainingCandidatesCount(results) == 0)
{
// UNDONE: Extension methods!
return;
}

// SPEC: The best method of the set of candidate methods is identified. If a single best method cannot be identified,
// SPEC: the method invocation is ambiguous, and a binding-time error occurs.
// SPEC: The best method of the set of candidate methods is identified. If a single best method cannot be identified,
// SPEC: the method invocation is ambiguous, and a binding-time error occurs.

RemoveWorseMembers(results, arguments, ref useSiteDiagnostics);

Expand Down Expand Up @@ -494,7 +493,7 @@ private void AddMemberToCandidateSet<TMember>(
// Second, we need to determine if the method is applicable in its normal form or its expanded form.

var normalResult = (allowUnexpandedForm || !IsValidParams(leastOverriddenMember))
? IsMemberApplicableInNormalForm(member, leastOverriddenMember, typeArguments, arguments, isMethodGroupConversion, allowRefOmittedArguments, inferWithDynamic, ref useSiteDiagnostics)
? IsMemberApplicableInNormalForm(member, leastOverriddenMember, typeArguments, arguments, isMethodGroupConversion, allowRefOmittedArguments, inferWithDynamic, ref useSiteDiagnostics, completeResults: completeResults)
: default(MemberResolutionResult<TMember>);

var result = normalResult;
Expand Down Expand Up @@ -2511,15 +2510,22 @@ internal MemberResolutionResult<TMember> IsMemberApplicableInNormalForm<TMember>
bool isMethodGroupConversion,
bool allowRefOmittedArguments,
bool inferWithDynamic,
ref HashSet<DiagnosticInfo> useSiteDiagnostics)
ref HashSet<DiagnosticInfo> useSiteDiagnostics,
bool completeResults = false)
where TMember : Symbol
{
// AnalyzeArguments matches arguments to parameter names and positions.
// For that purpose we use the most derived member.
var argumentAnalysis = AnalyzeArguments(member, arguments, isMethodGroupConversion, expanded: false);
if (!argumentAnalysis.IsValid)
{
return new MemberResolutionResult<TMember>(member, leastOverriddenMember, MemberAnalysisResult.ArgumentParameterMismatch(argumentAnalysis));
// When we are producing more complete results, and a required parameter is missing, we push on
// to type inference so that lambda arguments can be bound to their delegate-typed parameters,
// thus improving the API and intellisense experience.
if (!completeResults || argumentAnalysis.Kind != ArgumentAnalysisResultKind.RequiredParameterMissing)
{
return new MemberResolutionResult<TMember>(member, leastOverriddenMember, MemberAnalysisResult.ArgumentParameterMismatch(argumentAnalysis));
}
}

// Check after argument analysis, but before more complicated type inference and argument type validation.
Expand Down Expand Up @@ -2552,12 +2558,21 @@ internal MemberResolutionResult<TMember> IsMemberApplicableInNormalForm<TMember>

// The member passed to the following call is returned in the result (possibly a constructed version of it).
// The applicability is checked based on effective parameters passed in.
return IsApplicable(
var applicableResult = IsApplicable(
member, leastOverriddenMember,
typeArguments, arguments, originalEffectiveParameters, constructedEffectiveParameters,
argumentAnalysis.ArgsToParamsOpt, hasAnyRefOmittedArgument,
ref useSiteDiagnostics,
inferWithDynamic);

// If we were producing complete results and had missing arguments, we pushed on in order to call IsApplicable for
// type inference and lambda binding. In that case we still need to return the argument mismatch failure here.
if (completeResults && !argumentAnalysis.IsValid)
{
return new MemberResolutionResult<TMember>(member, leastOverriddenMember, MemberAnalysisResult.ArgumentParameterMismatch(argumentAnalysis));
}

return applicableResult;
}

private MemberResolutionResult<TMember> IsMemberApplicableInExpandedForm<TMember>(
Expand Down Expand Up @@ -2802,7 +2817,17 @@ private MemberAnalysisResult IsApplicable(
// We add a "virtual parameter" for the __arglist.
int paramCount = parameters.ParameterTypes.Length + (isVararg ? 1 : 0);

Debug.Assert(paramCount == arguments.Arguments.Count);
if (arguments.Arguments.Count < paramCount)
{
// For improved error recovery, we perform type inference even when the argument
// list is of the wrong length. The caller is expected to detect and handle that,
// treating the method as inapplicable.
paramCount = arguments.Arguments.Count;
}
else
{
Debug.Assert(paramCount == arguments.Arguments.Count);
}

// For each argument in A, the parameter passing mode of the argument (i.e., value, ref, or out) is
// identical to the parameter passing mode of the corresponding parameter, and
Expand Down
186 changes: 184 additions & 2 deletions src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1768,8 +1768,7 @@ public class MyArgumentType
// (6,57): error CS1002: ; expected
// var handler = new MyDelegateType((s, e) => { e. });
Diagnostic(ErrorCode.ERR_SemicolonExpected, "}").WithLocation(6, 57)
)
;
);
var tree = compilation.SyntaxTrees[0];
var sm = compilation.GetSemanticModel(tree);
var lambda = tree.GetCompilationUnitRoot().DescendantNodes().OfType<LambdaExpressionSyntax>().Single();
Expand All @@ -1780,6 +1779,189 @@ public class MyArgumentType
Assert.Equal(TypeKind.Class, typeInfo.Type.TypeKind);
Assert.NotEmpty(typeInfo.Type.GetMembers("SomePublicMember"));
}

[Fact]
[WorkItem(11053, "https://github.com/dotnet/roslyn/issues/11053")]
[WorkItem(11358, "https://github.com/dotnet/roslyn/issues/11358")]
public void TestLambdaWithError07()
{
var source =
@"using System;
using System.Collections.Generic;

public static class Program
{
public static void Main()
{
var parameter = new List<string>();
var result = parameter.FirstOrDefault(x => x. );
}
}

public static class Enumerable
{
public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source, TSource defaultValue)
{
return default(TSource);
}

public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate, TSource defaultValue)
{
return default(TSource);
}
}";
var compilation = CreateCompilationWithMscorlibAndSystemCore(source).VerifyDiagnostics(
// (9,55): error CS1001: Identifier expected
// var result = parameter.FirstOrDefault(x => x. );
Diagnostic(ErrorCode.ERR_IdentifierExpected, ")").WithLocation(9, 55)
);
var tree = compilation.SyntaxTrees[0];
var sm = compilation.GetSemanticModel(tree);
var lambda = tree.GetCompilationUnitRoot().DescendantNodes().OfType<LambdaExpressionSyntax>().Single();
var eReference = lambda.Body.DescendantNodes().OfType<IdentifierNameSyntax>().First();
Assert.Equal("x", eReference.ToString());
var typeInfo = sm.GetTypeInfo(eReference);
Assert.Equal(TypeKind.Class, typeInfo.Type.TypeKind);
Assert.Equal("String", typeInfo.Type.Name);
Assert.NotEmpty(typeInfo.Type.GetMembers("Replace"));
}

[Fact]
[WorkItem(11053, "https://github.com/dotnet/roslyn/issues/11053")]
[WorkItem(11358, "https://github.com/dotnet/roslyn/issues/11358")]
public void TestLambdaWithError08()
{
var source =
@"using System;
using System.Collections.Generic;

public static class Program
{
public static void Main()
{
var parameter = new List<string>();
var result = parameter.FirstOrDefault(x => x. );
}
}

public static class Enumerable
{
public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source, params TSource[] defaultValue)
{
return default(TSource);
}

public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate, params TSource[] defaultValue)
{
return default(TSource);
}
}";
var compilation = CreateCompilationWithMscorlibAndSystemCore(source).VerifyDiagnostics(
// (9,55): error CS1001: Identifier expected
// var result = parameter.FirstOrDefault(x => x. );
Diagnostic(ErrorCode.ERR_IdentifierExpected, ")").WithLocation(9, 55)
);
var tree = compilation.SyntaxTrees[0];
var sm = compilation.GetSemanticModel(tree);
var lambda = tree.GetCompilationUnitRoot().DescendantNodes().OfType<LambdaExpressionSyntax>().Single();
var eReference = lambda.Body.DescendantNodes().OfType<IdentifierNameSyntax>().First();
Assert.Equal("x", eReference.ToString());
var typeInfo = sm.GetTypeInfo(eReference);
Assert.Equal(TypeKind.Class, typeInfo.Type.TypeKind);
Assert.Equal("String", typeInfo.Type.Name);
Assert.NotEmpty(typeInfo.Type.GetMembers("Replace"));
}

[Fact]
[WorkItem(11053, "https://github.com/dotnet/roslyn/issues/11053")]
[WorkItem(11358, "https://github.com/dotnet/roslyn/issues/11358")]
public void TestLambdaWithError09()
{
var source =
@"using System;

public static class Program
{
public static void Main()
{
var parameter = new MyList<string>();
var result = parameter.FirstOrDefault(x => x. );
}
}

public class MyList<TSource>
{
public TSource FirstOrDefault(TSource defaultValue)
{
return default(TSource);
}

public TSource FirstOrDefault(Func<TSource, bool> predicate, TSource defaultValue)
{
return default(TSource);
}
}
";
var compilation = CreateCompilationWithMscorlibAndSystemCore(source).VerifyDiagnostics(
// (8,55): error CS1001: Identifier expected
// var result = parameter.FirstOrDefault(x => x. );
Diagnostic(ErrorCode.ERR_IdentifierExpected, ")").WithLocation(8, 55)
);
var tree = compilation.SyntaxTrees[0];
var sm = compilation.GetSemanticModel(tree);
var lambda = tree.GetCompilationUnitRoot().DescendantNodes().OfType<LambdaExpressionSyntax>().Single();
var eReference = lambda.Body.DescendantNodes().OfType<IdentifierNameSyntax>().First();
Assert.Equal("x", eReference.ToString());
var typeInfo = sm.GetTypeInfo(eReference);
Assert.Equal(TypeKind.Class, typeInfo.Type.TypeKind);
Assert.Equal("String", typeInfo.Type.Name);
Assert.NotEmpty(typeInfo.Type.GetMembers("Replace"));
}

[Fact]
[WorkItem(11053, "https://github.com/dotnet/roslyn/issues/11053")]
[WorkItem(11358, "https://github.com/dotnet/roslyn/issues/11358")]
public void TestLambdaWithError10()
{
var source =
@"using System;

public static class Program
{
public static void Main()
{
var parameter = new MyList<string>();
var result = parameter.FirstOrDefault(x => x. );
}
}

public class MyList<TSource>
{
public TSource FirstOrDefault(params TSource[] defaultValue)
{
return default(TSource);
}

public TSource FirstOrDefault(Func<TSource, bool> predicate, params TSource[] defaultValue)
{
return default(TSource);
}
}
";
var compilation = CreateCompilationWithMscorlibAndSystemCore(source).VerifyDiagnostics(
// (8,55): error CS1001: Identifier expected
// var result = parameter.FirstOrDefault(x => x. );
Diagnostic(ErrorCode.ERR_IdentifierExpected, ")").WithLocation(8, 55)
);
var tree = compilation.SyntaxTrees[0];
var sm = compilation.GetSemanticModel(tree);
var lambda = tree.GetCompilationUnitRoot().DescendantNodes().OfType<LambdaExpressionSyntax>().Single();
var eReference = lambda.Body.DescendantNodes().OfType<IdentifierNameSyntax>().First();
Assert.Equal("x", eReference.ToString());
var typeInfo = sm.GetTypeInfo(eReference);
Assert.Equal(TypeKind.Class, typeInfo.Type.TypeKind);
Assert.Equal("String", typeInfo.Type.Name);
Assert.NotEmpty(typeInfo.Type.GetMembers("Replace"));
}
}
}