Skip to content

Commit

Permalink
Merge pull request #11718 from gafter/master-errlambda02
Browse files Browse the repository at this point in the history
Attempt type inference when there are no applicable methods
  • Loading branch information
gafter committed Jun 3, 2016
2 parents c346e48 + f70ec79 commit 07984da
Show file tree
Hide file tree
Showing 3 changed files with 220 additions and 50 deletions.
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"));
}
}
}

0 comments on commit 07984da

Please sign in to comment.