-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Extensions: misc checks on receiver parameter and extension members #77937
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
9fe5bae
e74ab5a
ef195e9
eb23458
5f4ea9d
1b01943
a956b6b
40195fc
18f7aee
54168f1
f4c08f2
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 |
|---|---|---|
|
|
@@ -107,14 +107,21 @@ public static void ValidateIteratorMethod(CSharpCompilation compilation, MethodS | |
| return; | ||
| } | ||
|
|
||
| foreach (var parameter in iterator.Parameters) | ||
| var parameters = !iterator.IsStatic | ||
| ? iterator.GetParametersIncludingExtensionParameter() | ||
|
Member
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. Should the
Member
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. That would not be convenient. That method is also used in overload resolution, and for the purpose of overload resolution, we do want to account for the extension parameter even when the method is static (the static receiver counts as an argument in that case). |
||
| : iterator.Parameters; | ||
|
|
||
| foreach (var parameter in parameters) | ||
| { | ||
| bool isReceiverParameter = parameter.IsExtensionParameter(); | ||
| if (parameter.RefKind != RefKind.None) | ||
| { | ||
| diagnostics.Add(ErrorCode.ERR_BadIteratorArgType, parameter.GetFirstLocation()); | ||
| var location = isReceiverParameter ? iterator.GetFirstLocation() : parameter.GetFirstLocation(); | ||
| diagnostics.Add(ErrorCode.ERR_BadIteratorArgType, location); | ||
| } | ||
| else if (parameter.Type.IsPointerOrFunctionPointer()) | ||
| else if (parameter.Type.IsPointerOrFunctionPointer() && !isReceiverParameter) | ||
| { | ||
| // We already reported an error elsewhere if the receiver parameter of an extension is a pointer type. | ||
| diagnostics.Add(ErrorCode.ERR_UnsafeIteratorArgType, parameter.GetFirstLocation()); | ||
| } | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -139,7 +139,7 @@ protected override void GenerateControlFields() | |
| // Add a field: bool disposeMode | ||
| _disposeModeField = F.StateMachineField(boolType, GeneratedNames.MakeDisposeModeFieldName()); | ||
|
|
||
| if (_isEnumerable && this.method.Parameters.Any(static p => p.IsSourceParameterWithEnumeratorCancellationAttribute())) | ||
| if (_isEnumerable && this.method.Parameters.Any(static p => !p.IsExtensionParameterImplementation() && p.HasEnumeratorCancellationAttribute)) | ||
|
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.
Member
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. Do you mean we should leave a comment here (the attribute is ignored on extension parameter) or do you mean you think the attribute should be effective on the extension parameter too? The reason I don't think the attribute should be effective is that this attribute is very specific to async-iterator method implementations. I don't think it belongs on an extension parameter, as the extension block may contain many kinds of members. Also, the scenario where you need an async-iterator extension method on a
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 am fine with this design decision, Could you please mention this explicitly in the speclet? Also, please make sure to test scenario where extension parameter implementation is the only parameter for the method.
Member
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. Sounds good. Linked to corresponding spec update.
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.
Are you saying the
Member
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. That's correct. The only restriction is that only one parameter may be marked with this attribute. |
||
| { | ||
| // Add a field: CancellationTokenSource combinedTokens | ||
| _combinedTokensField = F.StateMachineField( | ||
|
|
@@ -211,7 +211,8 @@ protected override BoundStatement InitializeParameterField(MethodSymbol getEnume | |
| { | ||
| BoundStatement result; | ||
| if (_combinedTokensField is object && | ||
| parameter.IsSourceParameterWithEnumeratorCancellationAttribute() && | ||
| !parameter.IsExtensionParameterImplementation() && | ||
| parameter.HasEnumeratorCancellationAttribute && | ||
| parameter.Type.Equals(F.Compilation.GetWellKnownType(WellKnownType.System_Threading_CancellationToken), TypeCompareKind.ConsiderEverything)) | ||
| { | ||
| // For a parameter of type CancellationToken with [EnumeratorCancellation] | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -78,6 +78,30 @@ protected override (TypeWithAnnotations ReturnType, ImmutableArray<ParameterSymb | |
| if (parameter is { }) | ||
| { | ||
| checkUnderspecifiedGenericExtension(parameter, ContainingType.TypeParameters, diagnostics); | ||
|
|
||
| TypeSymbol parameterType = parameter.TypeWithAnnotations.Type; | ||
| RefKind parameterRefKind = parameter.RefKind; | ||
| SyntaxNode? parameterTypeSyntax = parameterList.Parameters[0].Type; | ||
| Debug.Assert(parameterTypeSyntax is not null); | ||
|
|
||
| // Note: SourceOrdinaryMethodSymbol.ExtensionMethodChecks has similar checks, which should be kept in sync. | ||
| if (!parameterType.IsValidExtensionParameterType()) | ||
| { | ||
| diagnostics.Add(ErrorCode.ERR_BadTypeforThis, parameterTypeSyntax, parameterType); | ||
| } | ||
| else if (parameterRefKind == RefKind.Ref && !parameterType.IsValueType) | ||
| { | ||
| diagnostics.Add(ErrorCode.ERR_RefExtensionParameterMustBeValueTypeOrConstrainedToOne, parameterTypeSyntax); | ||
| } | ||
| else if (parameterRefKind is RefKind.In or RefKind.RefReadOnlyParameter && parameterType.TypeKind != TypeKind.Struct) | ||
| { | ||
| diagnostics.Add(ErrorCode.ERR_InExtensionParameterMustBeValueType, parameterTypeSyntax); | ||
| } | ||
|
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 think it would be desirable to try sharing logic with
Member
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 agree and I tried that initially, but the logic for these three checks in
Member
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 leaving a comment in both places saying that we should keep them in sync. |
||
|
|
||
| if (parameter.Name is "" && parameterRefKind != RefKind.None) | ||
|
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.
Member
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. Yes, but that's currently a parsing error. See |
||
| { | ||
| diagnostics.Add(ErrorCode.ERR_ModifierOnUnnamedReceiverParameter, parameterTypeSyntax); | ||
| } | ||
| } | ||
|
|
||
| if (parameter is { Name: var name } && name != "" && | ||
|
|
||
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.
Shouldn't one be able to use this word? #Closed
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.
Not in the
mainbranch