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

Support modifiers with simple lambda parameters. #75400

Merged
merged 126 commits into from
Jan 16, 2025
Merged
Show file tree
Hide file tree
Changes from 116 commits
Commits
Show all changes
126 commits
Select commit Hold shift + click to select a range
5ef8b62
Support ref lambdas
CyrusNajmabadi Oct 4, 2024
377fd39
parsing and tests
CyrusNajmabadi Oct 4, 2024
71258cb
Update tests
CyrusNajmabadi Oct 4, 2024
fc3889c
Parsing
CyrusNajmabadi Oct 4, 2024
df0fcd3
IN progress
CyrusNajmabadi Oct 4, 2024
9be9229
IN progress
CyrusNajmabadi Oct 4, 2024
7b8a22a
IN progress
CyrusNajmabadi Oct 4, 2024
5ac1ae3
IN progress
CyrusNajmabadi Oct 4, 2024
da298d8
Simplify
CyrusNajmabadi Oct 4, 2024
fcced9f
Update tst
CyrusNajmabadi Oct 4, 2024
1ff139d
Add tests
CyrusNajmabadi Oct 4, 2024
a5ac0b2
Merge branch 'main' into refLambdas
CyrusNajmabadi Oct 4, 2024
1381610
Fix tests
CyrusNajmabadi Oct 5, 2024
30dde1f
Merge remote-tracking branch 'upstream/main' into refLambdas
CyrusNajmabadi Oct 7, 2024
138cc1b
Fix tests
CyrusNajmabadi Oct 7, 2024
4e3d787
remove
CyrusNajmabadi Oct 7, 2024
06a65d4
restore
CyrusNajmabadi Oct 7, 2024
2bf5192
Fix tests
CyrusNajmabadi Oct 7, 2024
ec0f272
reorder
CyrusNajmabadi Oct 7, 2024
480eacb
simplify
CyrusNajmabadi Oct 7, 2024
987928e
simplify
CyrusNajmabadi Oct 7, 2024
08cdb3b
REvert
CyrusNajmabadi Oct 7, 2024
86b9cda
Simplify parsing
CyrusNajmabadi Oct 7, 2024
6c07d0c
remove unused usings
CyrusNajmabadi Oct 7, 2024
fbc0b05
Doc and rename
CyrusNajmabadi Oct 7, 2024
ec51315
Rename
CyrusNajmabadi Oct 7, 2024
b7edb45
Docs
CyrusNajmabadi Oct 7, 2024
66ed2a0
Fix errors
CyrusNajmabadi Oct 7, 2024
223641a
Fix errors
CyrusNajmabadi Oct 7, 2024
7995a7e
Fixup tests
CyrusNajmabadi Oct 7, 2024
c60680d
Simplify
CyrusNajmabadi Oct 7, 2024
922ec4f
Reorder
CyrusNajmabadi Oct 7, 2024
b0e61fb
Test fixes
CyrusNajmabadi Oct 8, 2024
3d5bd38
Fix tests
CyrusNajmabadi Oct 8, 2024
0fa7e52
Fix tests
CyrusNajmabadi Oct 9, 2024
4d888f6
Merge branch 'main' into refLambdas
CyrusNajmabadi Nov 4, 2024
5b7ac1a
Merge branch 'main' into refLambdas
CyrusNajmabadi Nov 26, 2024
b1c0415
Merge remote-tracking branch 'upstream/main' into refLambdas
CyrusNajmabadi Nov 27, 2024
0c62f86
Fix
CyrusNajmabadi Nov 27, 2024
02376ed
Update src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs
CyrusNajmabadi Nov 28, 2024
8d112ba
Update src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs
CyrusNajmabadi Nov 28, 2024
72cc5f2
Merge remote-tracking branch 'upstream/main' into refLambdas
CyrusNajmabadi Nov 28, 2024
b6cd56c
Add test
CyrusNajmabadi Nov 28, 2024
5624428
Fix
CyrusNajmabadi Nov 28, 2024
e93792e
docs
CyrusNajmabadi Nov 28, 2024
955d4fa
docs
CyrusNajmabadi Nov 28, 2024
b5f28b7
revert
CyrusNajmabadi Nov 28, 2024
bb74038
Ad test
CyrusNajmabadi Nov 28, 2024
47ed0e4
Simplify check
CyrusNajmabadi Nov 28, 2024
314d807
Fix comment
CyrusNajmabadi Nov 28, 2024
163a667
Add assert
CyrusNajmabadi Nov 28, 2024
c0c55ce
Use proper check
CyrusNajmabadi Nov 28, 2024
e7b4200
Add test
CyrusNajmabadi Nov 28, 2024
5654594
Add test
CyrusNajmabadi Nov 28, 2024
a6caa89
add teest
CyrusNajmabadi Nov 28, 2024
e0f855f
Merge remote-tracking branch 'upstream/main' into refLambdas
CyrusNajmabadi Dec 2, 2024
a690f4a
Merge remote-tracking branch 'upstream/main' into refLambdas
CyrusNajmabadi Dec 2, 2024
08b804f
Move check inwards
CyrusNajmabadi Dec 2, 2024
55f8a48
Support 'params'
CyrusNajmabadi Dec 2, 2024
7e7cd11
Update test
CyrusNajmabadi Dec 2, 2024
980ff5e
Add tests
CyrusNajmabadi Dec 2, 2024
6975e28
Revert
CyrusNajmabadi Dec 2, 2024
ee34ba8
Add c# 13 tests for params
CyrusNajmabadi Dec 2, 2024
e4e33aa
Fix
CyrusNajmabadi Dec 2, 2024
111ec5f
Simplify
CyrusNajmabadi Dec 2, 2024
06a8fca
Add assert back in
CyrusNajmabadi Dec 2, 2024
d9766a7
Update src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs
CyrusNajmabadi Dec 2, 2024
47ab140
Fix comment
CyrusNajmabadi Dec 2, 2024
dee332d
Merge branch 'refLambdas' of https://github.com/CyrusNajmabadi/roslyn…
CyrusNajmabadi Dec 2, 2024
d2cdfc3
Update src/Compilers/CSharp/Portable/Parser/LanguageParser.cs
CyrusNajmabadi Dec 2, 2024
e4464e0
Merge remote-tracking branch 'upstream/main' into refLambdas
CyrusNajmabadi Dec 3, 2024
b058429
Upda tecomment
CyrusNajmabadi Dec 3, 2024
f150417
Rename and add comments
CyrusNajmabadi Dec 3, 2024
729c639
Merge remote-tracking branch 'upstream/main' into refLambdas
CyrusNajmabadi Dec 3, 2024
ccf7bbc
Simplify
CyrusNajmabadi Dec 3, 2024
f65ff60
Update src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs
CyrusNajmabadi Dec 3, 2024
bd64fc2
Make theory
CyrusNajmabadi Dec 3, 2024
f4dc8f5
Merge branch 'main' into refLambdas
CyrusNajmabadi Dec 20, 2024
72b419d
Add testS
CyrusNajmabadi Dec 20, 2024
ea65299
Make 'scoped' always a lambda modifier in C# 14 and above
CyrusNajmabadi Dec 20, 2024
acb0f81
Add tests
CyrusNajmabadi Dec 20, 2024
c65d771
Add tests
CyrusNajmabadi Dec 20, 2024
da06993
Add tests
CyrusNajmabadi Dec 20, 2024
d45d1af
Add tests
CyrusNajmabadi Dec 20, 2024
79db98b
Add tests
CyrusNajmabadi Dec 20, 2024
6c7b931
Add tests
CyrusNajmabadi Dec 20, 2024
11a9e71
Add tests
CyrusNajmabadi Dec 20, 2024
63f688a
Add tests
CyrusNajmabadi Dec 20, 2024
fde87ac
Add tests
CyrusNajmabadi Dec 20, 2024
99482d3
Add tests
CyrusNajmabadi Dec 20, 2024
1596d26
Add tests
CyrusNajmabadi Dec 20, 2024
ef569dc
Add tests
CyrusNajmabadi Dec 20, 2024
005511f
Add tests
CyrusNajmabadi Dec 20, 2024
622efc9
Add tests
CyrusNajmabadi Dec 20, 2024
e6ba439
Add tests
CyrusNajmabadi Dec 20, 2024
09c0d2f
Add tests
CyrusNajmabadi Dec 20, 2024
48a8d96
Add tests
CyrusNajmabadi Dec 20, 2024
5a517e8
Add tests
CyrusNajmabadi Dec 20, 2024
1f0d95a
Add tests
CyrusNajmabadi Dec 20, 2024
62fed3a
Add tests
CyrusNajmabadi Dec 20, 2024
819ac94
Add tests
CyrusNajmabadi Dec 20, 2024
d1f8026
Merge branch 'main' into refLambdas
CyrusNajmabadi Jan 13, 2025
1e5d00e
Merge remote-tracking branch 'upstream/main' into refLambdas
CyrusNajmabadi Jan 13, 2025
87d005d
Explicit preview check
CyrusNajmabadi Jan 13, 2025
f43f02b
REmove usings
CyrusNajmabadi Jan 13, 2025
38b7c98
NRT
CyrusNajmabadi Jan 13, 2025
7851db3
Break into separate tests
CyrusNajmabadi Jan 13, 2025
2093c05
Add output tests
CyrusNajmabadi Jan 13, 2025
1aa702c
Update src/Compilers/CSharp/Test/Semantic/Semantics/SimpleLambdaParam…
CyrusNajmabadi Jan 13, 2025
cf775df
Add ioptree
CyrusNajmabadi Jan 13, 2025
7c9ed31
Merge branch 'refLambdas' of https://github.com/CyrusNajmabadi/roslyn…
CyrusNajmabadi Jan 13, 2025
2b89528
lint
CyrusNajmabadi Jan 13, 2025
0b34018
Merge remote-tracking branch 'upstream/main' into refLambdas
CyrusNajmabadi Jan 15, 2025
34fc6e6
Disallow implicit lambdas with 'params'
CyrusNajmabadi Jan 15, 2025
a6f45d5
Add tests
CyrusNajmabadi Jan 15, 2025
44ed91b
Update breaking change doc
CyrusNajmabadi Jan 15, 2025
41bd895
Apply suggestions from code review
CyrusNajmabadi Jan 15, 2025
2906eb6
Update docs/compilers/CSharp/Compiler Breaking Changes - DotNet 10.md
CyrusNajmabadi Jan 15, 2025
84bfaa8
Apply suggestions from code review
CyrusNajmabadi Jan 15, 2025
b7aebc9
Merge remote-tracking branch 'upstream/main' into refLambdas
CyrusNajmabadi Jan 15, 2025
34de5f2
Fix doc comment
CyrusNajmabadi Jan 15, 2025
d3a4b0b
Add more scoped tests
CyrusNajmabadi Jan 15, 2025
e6d9d31
Add asserts
CyrusNajmabadi Jan 15, 2025
e51345c
Remove unnecessary test
CyrusNajmabadi Jan 15, 2025
eef4e94
Update src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems
CyrusNajmabadi Jan 15, 2025
952fc5a
Add test
CyrusNajmabadi Jan 15, 2025
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
23 changes: 23 additions & 0 deletions docs/compilers/CSharp/Compiler Breaking Changes - DotNet 10.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,29 @@

This document lists known breaking changes in Roslyn after .NET 9 general release (.NET SDK version 9.0.100) through .NET 10 general release (.NET SDK version 10.0.100).

## 'scoped' in a lambda parameter list is now always a modifier.
CyrusNajmabadi marked this conversation as resolved.
Show resolved Hide resolved
CyrusNajmabadi marked this conversation as resolved.
Show resolved Hide resolved

***Introduced in Visual Studio 2022 version 17.13***

C# 14 introduces the ability to write a lambda with parameter modifiers, without having to specify a parameter type:
https://github.com/dotnet/csharplang/blob/main/proposals/simple-lambda-parameters-with-modifiers.md

As part of this work, a breaking change was accepted where `scoped` will always be treated as a modifier
in a lambda parameter, even where it might have been accepted as a type name in the past. For example:

```c#
// Legal if a type named 'scoped' was in scope (for example, from an external library)
CyrusNajmabadi marked this conversation as resolved.
Show resolved Hide resolved
var v = (scoped scoped s) => { ... };
CyrusNajmabadi marked this conversation as resolved.
Show resolved Hide resolved
```

In C# 14 this will be an error as both 'scoped' tokens are treated as modifiers. The workaround is to
use '@' in the type name position like so:
CyrusNajmabadi marked this conversation as resolved.
Show resolved Hide resolved
CyrusNajmabadi marked this conversation as resolved.
Show resolved Hide resolved

```c#
// Legal if a type named 'scoped' was in scope (for example, from an external library)
var v = (scoped @scoped s) => { ... };
CyrusNajmabadi marked this conversation as resolved.
Show resolved Hide resolved
CyrusNajmabadi marked this conversation as resolved.
Show resolved Hide resolved
```

## `Span<T>` and `ReadOnlySpan<T>` overloads are applicable in more scenarios in C# 14 and newer

***Introduced in Visual Studio 2022 version 17.13***
Expand Down
143 changes: 66 additions & 77 deletions src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
Expand All @@ -20,6 +20,7 @@ internal partial class Binder
// delegate (int x) { } (typed parameter list)
// x => ... (type-inferred parameter list)
// (x) => ... (type-inferred parameter list)
// (ref x) => ... (type-inferred parameter list with modifiers)
// (x, y) => ... (type-inferred parameter list)
// ( ) => ... (typed parameter list)
// (ref int x) => ... (typed parameter list)
Expand Down Expand Up @@ -52,7 +53,7 @@ private UnboundLambda AnalyzeAnonymousFunction(

var namesBuilder = ArrayBuilder<string>.GetInstance();
ImmutableArray<bool> discardsOpt = default;
SeparatedSyntaxList<ParameterSyntax>? parameterSyntaxList = null;
SeparatedSyntaxList<ParameterSyntax>? parameterSyntaxListOpt = null;
bool hasSignature;

if (syntax is LambdaExpressionSyntax lambdaSyntax)
Expand Down Expand Up @@ -80,8 +81,9 @@ private UnboundLambda AnalyzeAnonymousFunction(
{
(returnRefKind, returnType) = BindExplicitLambdaReturnType(returnTypeSyntax, diagnostics);
}
parameterSyntaxList = paren.ParameterList.Parameters;
CheckParenthesizedLambdaParameters(parameterSyntaxList.Value, diagnostics);

parameterSyntaxListOpt = paren.ParameterList.Parameters;
CheckParenthesizedLambdaParameters(parameterSyntaxListOpt.Value, diagnostics);
break;
case SyntaxKind.AnonymousMethodExpression:
// delegate (int x) { }
Expand All @@ -92,7 +94,7 @@ private UnboundLambda AnalyzeAnonymousFunction(
hasSignature = anon.ParameterList != null;
if (hasSignature)
{
parameterSyntaxList = anon.ParameterList!.Parameters;
parameterSyntaxListOpt = anon.ParameterList!.Parameters;
}

break;
Expand All @@ -115,9 +117,10 @@ private UnboundLambda AnalyzeAnonymousFunction(
}
}

if (parameterSyntaxList != null)
if (parameterSyntaxListOpt is { } parameterSyntaxList)
{
var hasExplicitlyTypedParameterList = true;
var isAnonymousMethod = syntax.IsKind(SyntaxKind.AnonymousMethodExpression);
var hasExplicitlyTypedParameterList = parameterSyntaxList.All(static p => p.Type != null);

var typesBuilder = ArrayBuilder<TypeWithAnnotations>.GetInstance();
var refKindsBuilder = ArrayBuilder<RefKind>.GetInstance();
Expand All @@ -135,20 +138,26 @@ private UnboundLambda AnalyzeAnonymousFunction(

int parameterCount = 0;
int underscoresCount = 0;
foreach (var p in parameterSyntaxList.Value)
int firstDefault = -1;
for (int i = 0, n = parameterSyntaxList.Count; i < n; i++)
{
parameterCount++;

var p = parameterSyntaxList[i];
if (p.Identifier.IsUnderscoreToken())
{
underscoresCount++;
}

checkAttributes(syntax, p.AttributeLists, diagnostics);

var isAnonymousMethod = syntax.IsKind(SyntaxKind.AnonymousMethodExpression);
if (p.Default != null)
{
if (firstDefault == -1)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this logic moved from teh caller. no need for it to do a secondary pass to report errors when it can just do that here.

{
firstDefault = i;
}

if (isAnonymousMethod)
{
Error(diagnostics, ErrorCode.ERR_DefaultValueNotAllowed, p.Default.EqualsToken);
Expand All @@ -165,43 +174,43 @@ private UnboundLambda AnalyzeAnonymousFunction(
continue;
}

var typeSyntax = p.Type;
TypeWithAnnotations type = default;
var refKind = RefKind.None;
var scope = ScopedKind.None;
var typeOpt = p.Type is not null ? BindType(p.Type, diagnostics) : default;

if (typeSyntax == null)
{
hasExplicitlyTypedParameterList = false;
}
else
var refKind = ParameterHelpers.GetModifiers(p.Modifiers, out _, out var paramsKeyword, out _, out var scope);
var isParams = paramsKeyword.Kind() != SyntaxKind.None;

ParameterHelpers.CheckParameterModifiers(p, diagnostics, parsingFunctionPointerParams: false,
parsingLambdaParams: !isAnonymousMethod,
parsingAnonymousMethodParams: isAnonymousMethod);

ParameterHelpers.ReportParameterErrors(
owner: null, p, ordinal: i, lastParameterIndex: n - 1, isParams: isParams, typeOpt,
refKind, containingSymbol: null, thisKeyword: default, paramsKeyword: paramsKeyword, firstDefault, diagnostics);

if (parameterCount == parameterSyntaxList.Count &&
paramsKeyword.Kind() != SyntaxKind.None &&
!typeOpt.IsDefault &&
typeOpt.IsSZArray())
{
type = BindType(typeSyntax, diagnostics);
ParameterHelpers.CheckParameterModifiers(p, diagnostics, parsingFunctionPointerParams: false,
parsingLambdaParams: !isAnonymousMethod,
parsingAnonymousMethodParams: isAnonymousMethod);
refKind = ParameterHelpers.GetModifiers(p.Modifiers, out _, out var paramsKeyword, out _, out scope);

var isLastParameter = parameterCount == parameterSyntaxList.Value.Count;
if (isLastParameter && paramsKeyword.Kind() != SyntaxKind.None && type.IsSZArray())
{
ReportUseSiteDiagnosticForSynthesizedAttribute(Compilation,
WellKnownMember.System_ParamArrayAttribute__ctor,
diagnostics,
paramsKeyword.GetLocation());
}
ReportUseSiteDiagnosticForSynthesizedAttribute(Compilation,
WellKnownMember.System_ParamArrayAttribute__ctor,
diagnostics,
paramsKeyword.GetLocation());
}

namesBuilder.Add(p.Identifier.ValueText);
typesBuilder.Add(type);
typesBuilder.Add(typeOpt);
refKindsBuilder.Add(refKind);
scopesBuilder.Add(scope);
attributesBuilder.Add(syntax.Kind() == SyntaxKind.ParenthesizedLambdaExpression ? p.AttributeLists : default);
defaultValueBuilder.Add(p.Default);
}

discardsOpt = computeDiscards(parameterSyntaxList.Value, underscoresCount);
discardsOpt = computeDiscards(parameterSyntaxListOpt.Value, underscoresCount);

// Only include the types if *all* the parameters had types. Otherwise, if there were no parameter
// types (or a mix of typed and untyped parameters) include no types. Note, in the latter case we will
CyrusNajmabadi marked this conversation as resolved.
Show resolved Hide resolved
// have already reported an error about this issue.
if (hasExplicitlyTypedParameterList)
{
types = typesBuilder.ToImmutable();
Expand Down Expand Up @@ -241,7 +250,7 @@ private UnboundLambda AnalyzeAnonymousFunction(

namesBuilder.Free();

return UnboundLambda.Create(syntax, this, diagnostics.AccumulatesDependencies, returnRefKind, returnType, parameterAttributes, refKinds, scopes, types, names, discardsOpt, parameterSyntaxList, defaultValues, isAsync: isAsync, isStatic: isStatic);
return UnboundLambda.Create(syntax, this, diagnostics.AccumulatesDependencies, returnRefKind, returnType, parameterAttributes, refKinds, scopes, types, names, discardsOpt, parameterSyntaxListOpt, defaultValues, isAsync: isAsync, isStatic: isStatic);

static ImmutableArray<bool> computeDiscards(SeparatedSyntaxList<ParameterSyntax> parameters, int underscoresCount)
{
Expand Down Expand Up @@ -308,39 +317,40 @@ private static void CheckParenthesizedLambdaParameters(
{
if (parameterSyntaxList.Count > 0)
{
var hasTypes = parameterSyntaxList[0].Type != null;
// If one parameter has a type, then all parameters must have a type.
var requiresTypes = parameterSyntaxList.Any(static p => p.Type != null);

checkForImplicitDefault(hasTypes, parameterSyntaxList[0], diagnostics);

for (int i = 1, n = parameterSyntaxList.Count; i < n; i++)
foreach (var parameter in parameterSyntaxList)
{
var parameter = parameterSyntaxList[i];

// Ignore parameters with missing names. We'll have already reported an error
// about them in the parser.
if (!parameter.Identifier.IsMissing)
{
var thisParameterHasType = parameter.Type != null;

if (hasTypes != thisParameterHasType)
if (requiresTypes)
{
diagnostics.Add(ErrorCode.ERR_InconsistentLambdaParameterUsage,
parameter.Type?.GetLocation() ?? parameter.Identifier.GetLocation());
if (parameter.Type is null)
{
diagnostics.Add(ErrorCode.ERR_InconsistentLambdaParameterUsage,
parameter.Identifier.GetLocation());
}
}
else
CyrusNajmabadi marked this conversation as resolved.
Show resolved Hide resolved
{
if (parameter.Default != null)
{
diagnostics.Add(ErrorCode.ERR_ImplicitlyTypedDefaultParameter,
parameter.Identifier.GetLocation(), parameter.Identifier.Text);
}
}

checkForImplicitDefault(thisParameterHasType, parameter, diagnostics);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

inlined. it's an easy check. when we're in the case where we're not requiring types (an implicit lambda) we cannot have default parameter values.

// Check if `(ref i) => ...` is supported by this language version.
if (parameter.Modifiers.Count > 0 && parameter.Type is null)
{
CheckFeatureAvailability(parameter, MessageID.IDS_FeatureSimpleLambdaParameterModifiers, diagnostics);
CyrusNajmabadi marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
}

static void checkForImplicitDefault(bool hasType, ParameterSyntax param, BindingDiagnosticBag diagnostics)
{
if (!hasType && param.Default != null)
{
diagnostics.Add(ErrorCode.ERR_ImplicitlyTypedDefaultParameter,
param.Identifier.GetLocation(), param.Identifier.Text);
}
}
}

private UnboundLambda BindAnonymousFunction(AnonymousFunctionExpressionSyntax syntax, BindingDiagnosticBag diagnostics)
Expand All @@ -350,27 +360,6 @@ private UnboundLambda BindAnonymousFunction(AnonymousFunctionExpressionSyntax sy

var lambda = AnalyzeAnonymousFunction(syntax, diagnostics);
var data = lambda.Data;
if (data.HasExplicitlyTypedParameterList)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this logic moved into AnalyzeAnonymousFunction. In particular, we want check parameters unilaterally even if there are types or no types (since we want to check modifier usage in the latter case). so the HasExplicitlyTypedParameterList had to go.

{
int firstDefault = -1;
for (int i = 0; i < lambda.ParameterCount; i++)
{
// paramSyntax should not be null here; we should always be operating on an anonymous function which will have parameter information
var paramSyntax = lambda.ParameterSyntax(i);
Debug.Assert(paramSyntax is { });
if (paramSyntax.Default != null && firstDefault == -1)
{
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?
ParameterHelpers.ReportParameterErrors(owner: null, paramSyntax, ordinal: i, lastParameterIndex: lambda.ParameterCount - 1, isParams: isParams, lambda.ParameterTypeWithAnnotations(i),
lambda.RefKind(i), containingSymbol: null, thisKeyword: default, paramsKeyword: paramsKeyword, firstDefault, diagnostics);
}
}

// Parser will only have accepted static/async as allowed modifiers on this construct.
// However, it may have accepted duplicates of those modifiers. Ensure that any dupes
Expand Down
48 changes: 26 additions & 22 deletions src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2148,18 +2148,33 @@ internal void GenerateAnonymousFunctionConversionError(BindingDiagnosticBag diag
// The simplest possible case is (x, y, z)=>whatever where the target type has a ref or out parameter.

var delegateParameters = delegateType.DelegateParameters();
if (reason == LambdaConversionResult.RefInImplicitlyTypedLambda)
if (reason == LambdaConversionResult.MismatchedParameterRefKind)
{
for (int i = 0; i < anonymousFunction.ParameterCount; ++i)
{
var delegateRefKind = delegateParameters[i].RefKind;
if (delegateRefKind != RefKind.None)
var lambdaRefKind = anonymousFunction.RefKind(i);

if (!OverloadResolution.AreRefsCompatibleForMethodConversion(
candidateMethodParameterRefKind: lambdaRefKind,
delegateParameterRefKind: delegateRefKind,
this.Compilation))
{
// Parameter {0} must be declared with the '{1}' keyword
Error(diagnostics, ErrorCode.ERR_BadParamRef, anonymousFunction.ParameterLocation(i),
i + 1, delegateRefKind.ToParameterDisplayString());
var lambdaParameterLocation = anonymousFunction.ParameterLocation(i);
if (delegateRefKind == RefKind.None)
{
// Parameter {0} should not be declared with the '{1}' keyword
Error(diagnostics, ErrorCode.ERR_BadParamExtraRef, lambdaParameterLocation, i + 1, lambdaRefKind.ToParameterDisplayString());
}
else
{
// Parameter {0} must be declared with the '{1}' keyword
Error(diagnostics, ErrorCode.ERR_BadParamRef, lambdaParameterLocation, i + 1, delegateRefKind.ToParameterDisplayString());
}
}
}

Debug.Assert(diagnostics.DiagnosticBag?.Count is not 0);
return;
CyrusNajmabadi marked this conversation as resolved.
Show resolved Hide resolved
}

Expand All @@ -2181,16 +2196,18 @@ internal void GenerateAnonymousFunctionConversionError(BindingDiagnosticBag diag

if (reason == LambdaConversionResult.MismatchedParameterType)
{
// This is checked in the code that returns LambdaConversionResult.MismatchedParameterType
Debug.Assert(anonymousFunction.HasExplicitlyTypedParameterList);

// Cannot convert {0} to type '{1}' because the parameter types do not match the delegate parameter types
conversionError(diagnostics, ErrorCode.ERR_CantConvAnonMethParams, id, targetType);
CyrusNajmabadi marked this conversation as resolved.
Show resolved Hide resolved
Debug.Assert(anonymousFunction.ParameterCount == delegateParameters.Length);
for (int i = 0; i < anonymousFunction.ParameterCount; ++i)
{
var lambdaParameterType = anonymousFunction.ParameterType(i);
if (lambdaParameterType.IsErrorType())
{
continue;
}
Copy link
Member Author

@CyrusNajmabadi CyrusNajmabadi Oct 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this waas already checked above on line 2107. no need to check again.


// Can't be an error type. This was already checked in a loop above this one.
Debug.Assert(!lambdaParameterType.IsErrorType());

var lambdaParameterLocation = anonymousFunction.ParameterLocation(i);
var lambdaRefKind = anonymousFunction.RefKind(i);
Expand All @@ -2205,19 +2222,6 @@ internal void GenerateAnonymousFunctionConversionError(BindingDiagnosticBag diag
Error(diagnostics, ErrorCode.ERR_BadParamType, lambdaParameterLocation,
i + 1, lambdaRefKind.ToParameterPrefix(), distinguisher.First, delegateRefKind.ToParameterPrefix(), distinguisher.Second);
jjonescz marked this conversation as resolved.
Show resolved Hide resolved
}
else if (lambdaRefKind != delegateRefKind)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this moved upwards to an earlier error location.

{
if (delegateRefKind == RefKind.None)
{
// Parameter {0} should not be declared with the '{1}' keyword
Error(diagnostics, ErrorCode.ERR_BadParamExtraRef, lambdaParameterLocation, i + 1, lambdaRefKind.ToParameterDisplayString());
}
else
{
// Parameter {0} must be declared with the '{1}' keyword
Error(diagnostics, ErrorCode.ERR_BadParamRef, lambdaParameterLocation, i + 1, delegateRefKind.ToParameterDisplayString());
}
}
}
return;
}
Expand Down
Loading
Loading