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

Report structural lambda params errors #64960

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
8 changes: 5 additions & 3 deletions src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand Down
32 changes: 14 additions & 18 deletions src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,6 @@ private static ImmutableArray<TParameterSymbol> MakeParameters<TParameterSyntax,
int firstDefault = -1;

var builder = ArrayBuilder<TParameterSymbol>.GetInstance();
var mustBeLastParameter = (ParameterSyntax)null;

foreach (var parameterSyntax in parametersList)
{
Expand All @@ -148,13 +147,6 @@ private static ImmutableArray<TParameterSymbol> MakeParameters<TParameterSyntax,

if (parameterSyntax is ParameterSyntax concreteParam)
{
if (mustBeLastParameter == null &&
(concreteParam.Modifiers.Any(SyntaxKind.ParamsKeyword) ||
concreteParam.Identifier.Kind() == SyntaxKind.ArgListKeyword))
{
mustBeLastParameter = concreteParam;
}

if (concreteParam.IsArgList)
{
arglistToken = concreteParam.Identifier;
Expand All @@ -168,6 +160,12 @@ private static ImmutableArray<TParameterSymbol> MakeParameters<TParameterSyntax,
diagnostics.Add(ErrorCode.ERR_IllegalVarArgs, arglistToken.GetLocation());
}

if (parameterIndex != lastIndex)
{
// CS0257: An __arglist parameter must be the last parameter in a parameter list
diagnostics.Add(ErrorCode.ERR_VarargsLast, concreteParam.GetLocation());
}

continue;
}

Expand All @@ -191,22 +189,13 @@ private static ImmutableArray<TParameterSymbol> MakeParameters<TParameterSyntax,
TParameterSymbol parameter = parameterCreationFunc(withTypeParametersBinder, owner, parameterType, parameterSyntax, refKind, parameterIndex, paramsKeyword, thisKeyword, addRefReadOnlyModifier, scope, diagnostics);

DeclarationScope? declaredScope = parameter is SourceParameterSymbol s ? s.DeclaredScope : null;
ReportParameterErrors(owner, parameterSyntax, parameter.Ordinal, parameter.IsParams, parameter.TypeWithAnnotations,
ReportParameterErrors(owner, parameterSyntax, parameter.Ordinal, lastParameterIndex: lastIndex, parameter.IsParams, parameter.TypeWithAnnotations,
parameter.RefKind, declaredScope, parameter.ContainingSymbol, thisKeyword, paramsKeyword, firstDefault, diagnostics);

builder.Add(parameter);
++parameterIndex;
}

if (mustBeLastParameter != null && mustBeLastParameter != parametersList[lastIndex])
{
diagnostics.Add(
mustBeLastParameter.Identifier.Kind() == SyntaxKind.ArgListKeyword
? ErrorCode.ERR_VarargsLast
: ErrorCode.ERR_ParamsLast,
mustBeLastParameter.GetLocation());
}

ImmutableArray<TParameterSymbol> parameters = builder.ToImmutableAndFree();

if (!parsingFunctionPointer)
Expand Down Expand Up @@ -620,6 +609,7 @@ public static void ReportParameterErrors(
Symbol? owner,
BaseParameterSyntax syntax,
int ordinal,
int lastParameterIndex,
bool isParams,
TypeWithAnnotations typeWithAnnotations,
RefKind refKind,
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14305,10 +14305,7 @@ void M(int a, int b = 2, params int[] c) { }
Diagnostic(ErrorCode.WRN_OptionalParamValueMismatch, "b").WithArguments("2", "2", "<missing>").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));
}
}
}
59 changes: 55 additions & 4 deletions src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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()
jjonescz marked this conversation as resolved.
Show resolved Hide resolved
{
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));
}
jjonescz marked this conversation as resolved.
Show resolved Hide resolved

[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));
}
}
}