diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs index aeb400e0c0587..f345944053f91 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs @@ -339,10 +339,12 @@ private UnboundLambda BindAnonymousFunction(AnonymousFunctionExpressionSyntax sy firstDefault = i; } + ParameterHelpers.GetModifiers(paramSyntax.Modifiers, refnessKeyword: out _, out var paramsKeyword, thisKeyword: out _, scope: out _); + var isParams = paramsKeyword.Kind() != SyntaxKind.None; + // UNDONE: Where do we report improper use of pointer types? - // PROTOTYPE: Set `isParams` to report errors about them. - ParameterHelpers.ReportParameterErrors(owner: null, paramSyntax, ordinal: i, isParams: false, lambda.ParameterTypeWithAnnotations(i), - lambda.RefKind(i), lambda.DeclaredScope(i), containingSymbol: null, thisKeyword: default, paramsKeyword: default, firstDefault, diagnostics); + ParameterHelpers.ReportParameterErrors(owner: null, paramSyntax, ordinal: i, lastParameterIndex: lambda.ParameterCount - 1, isParams: isParams, lambda.ParameterTypeWithAnnotations(i), + lambda.RefKind(i), lambda.DeclaredScope(i), containingSymbol: null, thisKeyword: default, paramsKeyword: paramsKeyword, firstDefault, diagnostics); } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs b/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs index 2b11de5870c06..f3d2a952a1476 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs @@ -132,7 +132,6 @@ private static ImmutableArray MakeParameters.GetInstance(); - var mustBeLastParameter = (ParameterSyntax)null; foreach (var parameterSyntax in parametersList) { @@ -148,13 +147,6 @@ private static ImmutableArray MakeParameters MakeParameters MakeParameters parameters = builder.ToImmutableAndFree(); if (!parsingFunctionPointer) @@ -620,6 +609,7 @@ public static void ReportParameterErrors( Symbol? owner, BaseParameterSyntax syntax, int ordinal, + int lastParameterIndex, bool isParams, TypeWithAnnotations typeWithAnnotations, RefKind refKind, @@ -673,6 +663,12 @@ public static void ReportParameterErrors( diagnostics.Add(ErrorCode.ERR_MethodArgCantBeRefAny, syntax.Location, typeWithAnnotations.Type); } + if (isParams && ordinal != lastParameterIndex) + { + // error CS0231: A params parameter must be the last parameter in a parameter list + diagnostics.Add(ErrorCode.ERR_ParamsLast, syntax.GetLocation()); + } + if (declaredScope == DeclarationScope.ValueScoped && !typeWithAnnotations.IsRefLikeType()) { diagnostics.Add(ErrorCode.ERR_ScopedRefAndRefStructOnly, syntax.Location); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs index 698547df0b55a..130e0634dec90 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs @@ -14305,10 +14305,7 @@ void M(int a, int b = 2, params int[] c) { } Diagnostic(ErrorCode.WRN_OptionalParamValueMismatch, "b").WithArguments("2", "2", "").WithLocation(1, 20), // (1,40): warning CS9502: Parameter 3 has params modifier in lambda but not in target delegate type. // D d1 = (int a, int b = 2, params int[] c) => { }; // 1, 2 - Diagnostic(ErrorCode.WRN_ParamsArrayInLambdaOnly, "c").WithArguments("3").WithLocation(1, 40), - // (1,41): error CS1737: Optional parameters must appear after all required parameters - // D d1 = (int a, int b = 2, params int[] c) => { }; // 1, 2 - Diagnostic(ErrorCode.ERR_DefaultValueBeforeRequiredValue, ")").WithLocation(1, 41)); + Diagnostic(ErrorCode.WRN_ParamsArrayInLambdaOnly, "c").WithArguments("3").WithLocation(1, 40)); } } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs index 91cff4f49e563..b5ceab5115fb4 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs @@ -7921,10 +7921,7 @@ public static void Main() } } """; - CreateCompilation(source).VerifyDiagnostics( - // (5,48): error CS1737: Optional parameters must appear after all required parameters - // var lam = (int i = 3, params int[] args) => i; - Diagnostic(ErrorCode.ERR_DefaultValueBeforeRequiredValue, ")").WithLocation(5, 48)); + CreateCompilation(source).VerifyDiagnostics(); } [Fact] @@ -8260,5 +8257,59 @@ public void ParamsArray_Symbol_MultipleParamsArrays() Assert.False(((SourceParameterSymbol)lambdas[1].Parameters[1]).IsParams); Assert.True(((SourceParameterSymbol)lambdas[1].Parameters[2]).IsParams); } + + [Fact] + public void ParamsArray_NotLast() + { + var source = """ + var lam = (params int[] xs, int y) => xs.Length + y; + """; + CreateCompilation(source).VerifyDiagnostics( + // (1,12): error CS0231: A params parameter must be the last parameter in a parameter list + // var lam = (params int[] xs, int y) => xs.Length + y; + Diagnostic(ErrorCode.ERR_ParamsLast, "params int[] xs").WithLocation(1, 12), + // (1,25): warning CS9502: Parameter 1 has params modifier in lambda but not in target delegate type. + // var lam = (params int[] xs, int y) => xs.Length + y; + Diagnostic(ErrorCode.WRN_ParamsArrayInLambdaOnly, "xs").WithArguments("1").WithLocation(1, 25)); + } + + [Fact] + public void ParamsArray_Multiple() + { + var source = """ + var lam = (params int[] xs, params int[] ys) => xs.Length + ys.Length; + """; + CreateCompilation(source).VerifyDiagnostics( + // (1,12): error CS0231: A params parameter must be the last parameter in a parameter list + // var lam = (params int[] xs, params int[] ys) => xs.Length + ys.Length; + Diagnostic(ErrorCode.ERR_ParamsLast, "params int[] xs").WithLocation(1, 12), + // (1,25): warning CS9502: Parameter 1 has params modifier in lambda but not in target delegate type. + // var lam = (params int[] xs, params int[] ys) => xs.Length + ys.Length; + Diagnostic(ErrorCode.WRN_ParamsArrayInLambdaOnly, "xs").WithArguments("1").WithLocation(1, 25)); + } + + [Fact] + public void ParamsArray_NotArray() + { + var source = """ + var lam = (params int x) => x; + """; + CreateCompilation(source).VerifyDiagnostics( + // (1,12): error CS0225: The params parameter must be a single dimensional array + // var lam = (params int x) => x; + Diagnostic(ErrorCode.ERR_ParamsMustBeArray, "params").WithLocation(1, 12)); + } + + [Fact] + public void ParamsArray_ParamArrayAttribute() + { + var source = """ + var lam = ([System.ParamArrayAttribute] int[] xs) => xs; + """; + CreateCompilation(source).VerifyDiagnostics( + // (1,13): error CS0674: Do not use 'System.ParamArrayAttribute'. Use the 'params' keyword instead. + // var lam = ([System.ParamArrayAttribute] int[] xs) => xs; + Diagnostic(ErrorCode.ERR_ExplicitParamArray, "System.ParamArrayAttribute").WithLocation(1, 13)); + } } }