-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Implement lambda discard parameters #38786
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
Changes from all commits
268dea4
c8ba784
e8ed9dd
e9389c0
43e255f
6758a76
77dda47
570b3b0
63bbb77
161d3b8
658fd21
cadd8dc
1dd6cd3
3c99305
d30ee65
0d86ac4
f53b947
c2affd3
0e71122
84d2ae2
4ed09e9
29e4414
a3462ef
01b013c
aa0dff8
9d910b8
554d19c
07739c6
8f95c6b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -31,7 +31,7 @@ internal partial class Binder | |
| // If we have no modifiers then the modifiers array is null; if we have any modifiers | ||
| // then the modifiers array is non-null and not empty. | ||
|
|
||
| private (ImmutableArray<RefKind>, ImmutableArray<TypeWithAnnotations>, ImmutableArray<string>, bool) AnalyzeAnonymousFunction( | ||
| private UnboundLambda AnalyzeAnonymousFunction( | ||
| CSharpSyntaxNode syntax, DiagnosticBag diagnostics) | ||
| { | ||
| Debug.Assert(syntax != null); | ||
|
|
@@ -43,6 +43,7 @@ internal partial class Binder | |
| bool isAsync = false; | ||
|
|
||
| var namesBuilder = ArrayBuilder<string>.GetInstance(); | ||
| ImmutableArray<bool> discardsOpt = default; | ||
| SeparatedSyntaxList<ParameterSyntax>? parameterSyntaxList = null; | ||
| bool hasSignature; | ||
|
|
||
|
|
@@ -94,8 +95,14 @@ internal partial class Binder | |
| // However, we still want to give errors on every bad type in the list, even if one | ||
| // is missing. | ||
|
|
||
| int underscoresCount = 0; | ||
| foreach (var p in parameterSyntaxList.Value) | ||
| { | ||
| if (p.Identifier.IsUnderscoreToken()) | ||
| { | ||
| underscoresCount++; | ||
| } | ||
|
|
||
| foreach (var attributeList in p.AttributeLists) | ||
| { | ||
| Error(diagnostics, ErrorCode.ERR_AttributesNotAllowed, attributeList); | ||
|
|
@@ -161,6 +168,8 @@ internal partial class Binder | |
| refKindsBuilder.Add(refKind); | ||
| } | ||
|
|
||
| discardsOpt = computeDiscards(parameterSyntaxList.Value, underscoresCount); | ||
|
|
||
| if (hasExplicitlyTypedParameterList) | ||
| { | ||
| types = typesBuilder.ToImmutable(); | ||
|
|
@@ -182,7 +191,24 @@ internal partial class Binder | |
|
|
||
| namesBuilder.Free(); | ||
|
|
||
| return (refKinds, types, names, isAsync); | ||
| return new UnboundLambda(syntax, this, refKinds, types, names, discardsOpt, isAsync); | ||
|
|
||
| static ImmutableArray<bool> computeDiscards(SeparatedSyntaxList<ParameterSyntax> parameters, int underscoresCount) | ||
| { | ||
| if (underscoresCount <= 1) | ||
| { | ||
| return default; | ||
| } | ||
|
|
||
| // When there are two or more underscores, they are discards | ||
| var discardsBuilder = ArrayBuilder<bool>.GetInstance(parameters.Count); | ||
| foreach (var p in parameters) | ||
| { | ||
| discardsBuilder.Add(p.Identifier.IsUnderscoreToken()); | ||
| } | ||
|
|
||
| return discardsBuilder.ToImmutableAndFree(); | ||
| } | ||
| } | ||
|
|
||
| private void CheckParenthesizedLambdaParameters( | ||
|
|
@@ -216,25 +242,27 @@ private UnboundLambda BindAnonymousFunction(CSharpSyntaxNode syntax, DiagnosticB | |
| Debug.Assert(syntax != null); | ||
| Debug.Assert(syntax.IsAnonymousFunction()); | ||
|
|
||
| var (refKinds, types, names, isAsync) = AnalyzeAnonymousFunction(syntax, diagnostics); | ||
| if (!types.IsDefault) | ||
| var lambda = AnalyzeAnonymousFunction(syntax, diagnostics); | ||
| var data = lambda.Data; | ||
| if (data.HasExplicitlyTypedParameterList) | ||
| { | ||
| foreach (var type in types) | ||
| for (int i = 0; i < lambda.ParameterCount; i++) | ||
| { | ||
| // UNDONE: Where do we report improper use of pointer types? | ||
| var type = lambda.Data.ParameterTypeWithAnnotations(i); | ||
| if (type.HasType && type.IsStatic) | ||
| { | ||
| Error(diagnostics, ErrorCode.ERR_ParameterIsStaticClass, syntax, type.Type); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| var lambda = new UnboundLambda(syntax, this, refKinds, types, names, isAsync); | ||
| if (!names.IsDefault) | ||
| if (data.HasNames) | ||
| { | ||
| var binder = new LocalScopeBinder(this); | ||
| bool allowShadowingNames = binder.Compilation.IsFeatureEnabled(MessageID.IDS_FeatureNameShadowingInNestedFunctions); | ||
| var pNames = PooledHashSet<string>.GetInstance(); | ||
| bool seenDiscard = false; | ||
|
|
||
| for (int i = 0; i < lambda.ParameterCount; i++) | ||
| { | ||
|
|
@@ -245,6 +273,21 @@ private UnboundLambda BindAnonymousFunction(CSharpSyntaxNode syntax, DiagnosticB | |
| continue; | ||
| } | ||
|
|
||
| if (lambda.ParameterIsDiscard(i)) | ||
| { | ||
| if (seenDiscard) | ||
| { | ||
| // We only report the diagnostic on the second and subsequent underscores | ||
| MessageID.IDS_FeatureLambdaDiscardParameters.CheckFeatureAvailability( | ||
| diagnostics, | ||
| binder.Compilation, | ||
| lambda.ParameterLocation(i)); | ||
| } | ||
|
|
||
| seenDiscard = true; | ||
| continue; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Consider adding a test that would verify that we don't get here for a regular parameter named
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added test In reply to: 340741002 [](ancestors = 340741002)
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I doesn't look like the test is covering the scenario. I would expect an error about parameter having a conflicting name with something in an outer scope. The test is asserting a conflict of something declared in a nested scope with a parameter instead. Therefore, doesn't test the code path. In reply to: 341279707 [](ancestors = 341279707,340741002)
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm adding another test, but this In reply to: 341297823 [](ancestors = 341297823,341279707,340741002) |
||
| } | ||
|
|
||
| if (!pNames.Add(name)) | ||
| { | ||
| // The parameter name '{0}' is a duplicate | ||
|
|
||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -60,6 +60,11 @@ protected override sealed Symbol OriginalSymbolDefinition | |
| /// </summary> | ||
| public abstract RefKind RefKind { get; } | ||
|
|
||
| /// <summary> | ||
| /// Returns true if the parameter is a discard parameter. | ||
| /// </summary> | ||
| public abstract bool IsDiscard { get; } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Consider adding an explicit implementation for |
||
|
|
||
| /// <summary> | ||
| /// Custom modifiers associated with the ref modifier, or an empty array if there are none. | ||
| /// </summary> | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't feel valid to continue for the single discard case. #Closed