diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs
index f983aa0279cf5e1f7309cbc9d6d129e44fa37e5a..4c6ac8c156ae0a20cfcc22d8929edc696b890777 100644
--- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs
@@ -97,6 +97,12 @@ private enum Dependency
private Dependency[,] _dependencies; // Initialized lazily
private bool _dependenciesDirty;
+ ///
+ /// For error recovery, we allow a mismatch between the number of arguments and parameters
+ /// during type inference. This sometimes enables inferring the type for a lambda parameter.
+ ///
+ private int Length => System.Math.Min(_arguments.Length, _formalParameterTypes.Length);
+
public static MethodTypeInferenceResult Infer(
Binder binder,
ImmutableArray methodTypeParameters,
@@ -524,14 +530,13 @@ private void InferTypeArgsFirstPhase(Binder binder, ref HashSet
{
Debug.Assert(!_formalParameterTypes.IsDefault);
Debug.Assert(!_arguments.IsDefault);
- Debug.Assert(_arguments.Length == _formalParameterTypes.Length);
// We expect that we have been handed a list of arguments and a list of the
// formal parameter types they correspond to; all the details about named and
// optional parameters have already been dealt with.
// SPEC: For each of the method arguments Ei:
- for (int arg = 0; arg < _arguments.Length; arg++)
+ for (int arg = 0, length = this.Length; arg < length; arg++)
{
var argument = _arguments[arg];
@@ -795,7 +800,7 @@ private void MakeOutputTypeInferences(Binder binder, ref HashSet
// SPEC: where the output types contain unfixed type parameters but the input
// SPEC: types do not, an output type inference is made from Ei to Ti.
- for (int arg = 0; arg < _arguments.Length; arg++)
+ for (int arg = 0, length = this.Length; arg < length; arg++)
{
var formalType = _formalParameterTypes[arg];
var argument = _arguments[arg];
@@ -1067,7 +1072,7 @@ private bool DependsDirectlyOn(int iParam, int jParam)
Debug.Assert(IsUnfixed(iParam));
Debug.Assert(IsUnfixed(jParam));
- for (int iArg = 0; iArg < _arguments.Length; iArg++)
+ for (int iArg = 0, length = this.Length; iArg < length; iArg++)
{
var formalParameterType = _formalParameterTypes[iArg];
var argument = _arguments[iArg];
diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs
index c711907a2282487c3a9b7780684809bd19740356..0c3786843dcf63a798dea6c686f76a49bcccad28 100644
--- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs
@@ -2407,7 +2407,7 @@ internal EffectiveParameters(ImmutableArray types, ImmutableArray= parameters.Length)
{
continue;
}
@@ -2519,12 +2519,17 @@ private RefKind GetEffectiveParameterRefKind(ParameterSymbol parameter, RefKind
var argumentAnalysis = AnalyzeArguments(member, arguments, isMethodGroupConversion, expanded: false);
if (!argumentAnalysis.IsValid)
{
- // 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)
+ switch (argumentAnalysis.Kind)
{
- return new MemberResolutionResult(member, leastOverriddenMember, MemberAnalysisResult.ArgumentParameterMismatch(argumentAnalysis));
+ case ArgumentAnalysisResultKind.RequiredParameterMissing:
+ case ArgumentAnalysisResultKind.NoCorrespondingParameter:
+ if (!completeResults) goto default;
+ // When we are producing more complete results, and we have the wrong number of arguments, we push on
+ // through type inference so that lambda arguments can be bound to their delegate-typed parameters,
+ // thus improving the API and intellisense experience.
+ break;
+ default:
+ return new MemberResolutionResult(member, leastOverriddenMember, MemberAnalysisResult.ArgumentParameterMismatch(argumentAnalysis));
}
}
@@ -2824,10 +2829,6 @@ private RefKind GetEffectiveParameterRefKind(ParameterSymbol parameter, RefKind
// 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
diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs
index 5d9cd7ce5f13041da0409a958d27c3909ad852f1..9ad131333f850b72c61e4e1f5a740a975130308c 100644
--- a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs
+++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs
@@ -1963,5 +1963,62 @@ public TSource FirstOrDefault(Func predicate, params TSource[] de
Assert.Equal("String", typeInfo.Type.Name);
Assert.NotEmpty(typeInfo.Type.GetMembers("Replace"));
}
+
+ [Fact]
+ [WorkItem(557, "https://github.com/dotnet/roslyn/issues/557")]
+ public void TestLambdaWithError11()
+ {
+ var source =
+@"using System.Linq;
+
+public static class Program
+{
+ public static void Main()
+ {
+ var x = new {
+ X = """".Select(c => c.
+ Y = 0,
+ };
+ }
+}
+";
+ var compilation = CreateCompilationWithMscorlibAndSystemCore(source);
+ var tree = compilation.SyntaxTrees[0];
+ var sm = compilation.GetSemanticModel(tree);
+ var lambda = tree.GetCompilationUnitRoot().DescendantNodes().OfType().Single();
+ var eReference = lambda.Body.DescendantNodes().OfType().First();
+ Assert.Equal("c", eReference.ToString());
+ var typeInfo = sm.GetTypeInfo(eReference);
+ Assert.Equal(TypeKind.Struct, typeInfo.Type.TypeKind);
+ Assert.Equal("Char", typeInfo.Type.Name);
+ Assert.NotEmpty(typeInfo.Type.GetMembers("IsHighSurrogate")); // check it is the char we know and love
+ }
+
+ [Fact]
+ [WorkItem(5498, "https://github.com/dotnet/roslyn/issues/5498")]
+ public void TestLambdaWithError12()
+ {
+ var source =
+@"using System.Linq;
+
+class Program
+{
+ static void Main(string[] args)
+ {
+ var z = args.Select(a => a.
+ var foo =
+ }
+}";
+ var compilation = CreateCompilationWithMscorlibAndSystemCore(source);
+ var tree = compilation.SyntaxTrees[0];
+ var sm = compilation.GetSemanticModel(tree);
+ var lambda = tree.GetCompilationUnitRoot().DescendantNodes().OfType().Single();
+ var eReference = lambda.Body.DescendantNodes().OfType().First();
+ Assert.Equal("a", 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"));
+ }
}
}