提交 567d12f6 编写于 作者: G gafter

Improved error recovery for lambdas in a call with extra arguments.

Fixes #557, #5498
上级 b6e3176e
...@@ -97,6 +97,12 @@ private enum Dependency ...@@ -97,6 +97,12 @@ private enum Dependency
private Dependency[,] _dependencies; // Initialized lazily private Dependency[,] _dependencies; // Initialized lazily
private bool _dependenciesDirty; private bool _dependenciesDirty;
/// <summary>
/// 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.
/// </summary>
private int Length => System.Math.Min(_arguments.Length, _formalParameterTypes.Length);
public static MethodTypeInferenceResult Infer( public static MethodTypeInferenceResult Infer(
Binder binder, Binder binder,
ImmutableArray<TypeParameterSymbol> methodTypeParameters, ImmutableArray<TypeParameterSymbol> methodTypeParameters,
...@@ -524,14 +530,13 @@ private void InferTypeArgsFirstPhase(Binder binder, ref HashSet<DiagnosticInfo> ...@@ -524,14 +530,13 @@ private void InferTypeArgsFirstPhase(Binder binder, ref HashSet<DiagnosticInfo>
{ {
Debug.Assert(!_formalParameterTypes.IsDefault); Debug.Assert(!_formalParameterTypes.IsDefault);
Debug.Assert(!_arguments.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 // 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 // formal parameter types they correspond to; all the details about named and
// optional parameters have already been dealt with. // optional parameters have already been dealt with.
// SPEC: For each of the method arguments Ei: // 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]; var argument = _arguments[arg];
...@@ -795,7 +800,7 @@ private void MakeOutputTypeInferences(Binder binder, ref HashSet<DiagnosticInfo> ...@@ -795,7 +800,7 @@ private void MakeOutputTypeInferences(Binder binder, ref HashSet<DiagnosticInfo>
// SPEC: where the output types contain unfixed type parameters but the input // 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. // 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 formalType = _formalParameterTypes[arg];
var argument = _arguments[arg]; var argument = _arguments[arg];
...@@ -1067,7 +1072,7 @@ private bool DependsDirectlyOn(int iParam, int jParam) ...@@ -1067,7 +1072,7 @@ private bool DependsDirectlyOn(int iParam, int jParam)
Debug.Assert(IsUnfixed(iParam)); Debug.Assert(IsUnfixed(iParam));
Debug.Assert(IsUnfixed(jParam)); 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 formalParameterType = _formalParameterTypes[iArg];
var argument = _arguments[iArg]; var argument = _arguments[iArg];
......
...@@ -2407,7 +2407,7 @@ internal EffectiveParameters(ImmutableArray<TypeSymbol> types, ImmutableArray<Re ...@@ -2407,7 +2407,7 @@ internal EffectiveParameters(ImmutableArray<TypeSymbol> types, ImmutableArray<Re
{ {
int parm = argToParamMap.IsDefault ? arg : argToParamMap[arg]; int parm = argToParamMap.IsDefault ? arg : argToParamMap[arg];
// If this is the __arglist parameter, just skip it. // If this is the __arglist parameter, just skip it.
if (parm == parameters.Length) if (parm >= parameters.Length)
{ {
continue; continue;
} }
...@@ -2519,12 +2519,17 @@ private RefKind GetEffectiveParameterRefKind(ParameterSymbol parameter, RefKind ...@@ -2519,12 +2519,17 @@ private RefKind GetEffectiveParameterRefKind(ParameterSymbol parameter, RefKind
var argumentAnalysis = AnalyzeArguments(member, arguments, isMethodGroupConversion, expanded: false); var argumentAnalysis = AnalyzeArguments(member, arguments, isMethodGroupConversion, expanded: false);
if (!argumentAnalysis.IsValid) if (!argumentAnalysis.IsValid)
{ {
// When we are producing more complete results, and a required parameter is missing, we push on switch (argumentAnalysis.Kind)
// 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)); 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<TMember>(member, leastOverriddenMember, MemberAnalysisResult.ArgumentParameterMismatch(argumentAnalysis));
} }
} }
...@@ -2824,10 +2829,6 @@ private RefKind GetEffectiveParameterRefKind(ParameterSymbol parameter, RefKind ...@@ -2824,10 +2829,6 @@ private RefKind GetEffectiveParameterRefKind(ParameterSymbol parameter, RefKind
// treating the method as inapplicable. // treating the method as inapplicable.
paramCount = arguments.Arguments.Count; 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 // 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 // identical to the parameter passing mode of the corresponding parameter, and
......
...@@ -1963,5 +1963,62 @@ public TSource FirstOrDefault(Func<TSource, bool> predicate, params TSource[] de ...@@ -1963,5 +1963,62 @@ public TSource FirstOrDefault(Func<TSource, bool> predicate, params TSource[] de
Assert.Equal("String", typeInfo.Type.Name); Assert.Equal("String", typeInfo.Type.Name);
Assert.NotEmpty(typeInfo.Type.GetMembers("Replace")); 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<LambdaExpressionSyntax>().Single();
var eReference = lambda.Body.DescendantNodes().OfType<IdentifierNameSyntax>().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<LambdaExpressionSyntax>().Single();
var eReference = lambda.Body.DescendantNodes().OfType<IdentifierNameSyntax>().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"));
}
} }
} }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册