-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Extensions parsing #76867
base: features/extensions
Are you sure you want to change the base?
Extensions parsing #76867
Conversation
@@ -81,6 +81,7 @@ member_declaration | |||
| base_type_declaration | |||
| delegate_declaration | |||
| enum_member_declaration | |||
| extension_container |
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.
don't love that it's not called _declaration. #Resolved
@@ -348,6 +349,14 @@ delegate_declaration | |||
: attribute_list* modifier* 'delegate' type identifier_token type_parameter_list? parameter_list type_parameter_constraint_clause* ';' | |||
; | |||
|
|||
extension_container | |||
: attribute_list* modifier* syntax_token type_parameter_list? '(' receiver_parameter ')' type_parameter_constraint_clause* '{' member_declaration* '}' |
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.
syntax_token? not 'extension'
? #Resolved
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.
nit: allow an identifier. people will try to write it, and it will allow for better error recovery.
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.
nit: allow a parameter_list, as people will write more, and it will enable better error recovery.
; | ||
|
||
receiver_parameter | ||
: attribute_list* modifier* type identifier_token? |
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.
we should allow a parameter_syntax. it will make it much easier ot recover and represent throughout the system. (similar to how even a => a
uses a parameter_syntax).
overfitting syntax is often problematic. both for normal typing cases, and for expanding on this in the future. #Resolved
@@ -1732,7 +1732,7 @@ private TypeDeclarationSyntax ParseClassOrStructOrInterfaceDeclaration(SyntaxLis | |||
_termState |= TerminatorState.IsEndOfRecordOrClassOrStructOrInterfaceSignature; | |||
|
|||
var saveTerm = _termState; | |||
_termState |= TerminatorState.IsPossibleAggregateClauseStartOrStop; | |||
//_termState |= TerminatorState.IsPossibleAggregateClauseStartOrStop; // TODO2 looking for affected test |
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.
mark as prototype? so this doesn't get merged in? #Resolved
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.
This PR is draft. Not ready for review. TODO2 comments are flagged by our build and are useful to track things that need to be handled in the PR ;-)
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.
oh. gmail didn't mark this as draft. sorry!
gtk about TODO2. i didn't realize that.
@@ -1962,6 +1963,151 @@ static TypeDeclarationSyntax constructTypeDeclaration(ContextAwareSyntax syntaxF | |||
} | |||
} | |||
|
|||
private MemberDeclarationSyntax ParseExtensionContainer(SyntaxList<AttributeListSyntax> attributes, SyntaxListBuilder modifiers) |
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.
make return type strongly typed. #Resolved
var outerSaveTerm = _termState; | ||
_termState |= TerminatorState.IsPossibleAggregateClauseStartOrStop; // TODO2 add test affected | ||
|
||
TypeParameterListSyntax typeParameters = this.ParseTypeParameterList(); |
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.
check for identifier and recover gracefully. #Resolved
_termState |= TerminatorState.IsPossibleAggregateClauseStartOrStop; // TODO2 add test affected | ||
|
||
TypeParameterListSyntax typeParameters = this.ParseTypeParameterList(); | ||
ReceiverParameterSyntax receiverParameter = parseReceiverParameter(out var openParenToken, out var closeParenToken); |
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.
parse a parameter list, with a requirement that at least a single parameter is required. This can be done by updating ParseParameterList to take a parameter stating if a single parameter is required (and passing that to hte hlper it calls).
you can optionally error at parse time or binding time if there is >1 parameter. I recommend binding time. #Resolved
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.
By requiring a single parameter, you can be sure in later phases that it is safe to access [0]
_termState |= TerminatorState.IsEndOfRecordOrClassOrStructOrInterfaceSignature; | ||
constraints = _pool.Allocate<TypeParameterConstraintClauseSyntax>(); | ||
this.ParseTypeParameterConstraintClauses(constraints); | ||
} |
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.
extract this into helper. otehrwise it can get out of sync with type parameter parsing elsewhere. #Resolved
parseMembers = false; | ||
} | ||
|
||
if (parseMembers) |
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.
having a variable to just expand on if (!openBrace.IsMissing)
seems excessive. #Resolved
|
||
var modifiersList = (SyntaxList<SyntaxToken>)modifiers.ToList(); | ||
var membersList = (SyntaxList<MemberDeclarationSyntax>)members; | ||
var constraintsList = (SyntaxList<TypeParameterConstraintClauseSyntax>)constraints; |
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.
this is confusing. we have the .ToListAndFree helpers. Why not use those? then you don't need a try/finally either. #Resolved
} | ||
} | ||
|
||
if (openBrace.IsMissing) |
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.
here you check the openBrace, instead of parseMembers #Resolved
{ | ||
members = _pool.Allocate<MemberDeclarationSyntax>(); | ||
|
||
while (true) |
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.
afaict, this is a copy of ParseClassOrStructOrInterfaceDeclaration. Seems like we can just expand that to parse extensions, just with some extension-specific code. Note: as all teh rest support the primary cosntructor parameter list, this all falls out. The only difference that i can tell so far is simple that in the case of extensions we want at least one parameter (though this could be a binding-time check), and that we do not have an identifier. All of that seems similar to add to ParseClassOrStructOrInterfaceDeclaration vs this copy :) #Resolved
SyntaxToken? identifier; | ||
if (this.CurrentToken.Kind == SyntaxKind.CloseParenToken) | ||
{ | ||
identifier = null; |
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.
ah, you want identifiers to be optional. mixed feelings. but that feels very doable still :) #Resolved
@@ -43,5 +43,6 @@ public enum CompilerFeature | |||
RecordStructs, | |||
RequiredMembers, | |||
RefLifetime, | |||
Extensions, | |||
} | |||
} |
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.
incremental tests that changing class C(int i)
to extension(int i)
and vvice versa works would be good.
#Resolved
Overall, this looks quite good to me. My pref is to share more int eh parser. either higher level parsing functinos, or lower level shared routines :) #Resolved |
15cf64b
to
8e29f61
Compare
} | ||
|
||
/// <summary>Creates a new ExtensionDeclarationSyntax instance.</summary> | ||
public static ExtensionDeclarationSyntax ExtensionDeclaration(SyntaxList<AttributeListSyntax> attributeLists, SyntaxTokenList modifiers, SyntaxToken keyword, SyntaxToken identifier, TypeParameterListSyntax? typeParameterList, ParameterListSyntax? parameterList, BaseListSyntax? baseList, SyntaxList<TypeParameterConstraintClauseSyntax> constraintClauses, SyntaxList<MemberDeclarationSyntax> members) |
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.
Yes. I've created |
/// <item><description><see cref="SyntaxKind.ExtensionDeclaration"/></description></item> | ||
/// </list> | ||
/// </remarks> | ||
public sealed partial class ExtensionDeclarationSyntax : TypeDeclarationSyntax |
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.
skipBadParameterListTokens, | ||
allowTrailingSeparator: false, | ||
requireOneElement: false, | ||
requireOneElement: forExtension, // For extension declarations, we require one receiver parameter |
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.
requireOneElement: forExtension, // For extension declarations, we require one receiver parameter
It looks like the meaning of this parameter is to ensure that the syntax list is not empty, i.e. at least one item is expected. That doesn't mean that the helper won't accept more that one item. The comment, however, might be interpreted as though we instruct the helper to accept exactly one item (no more, no less). #Pending
|
||
SyntaxToken? identifier; | ||
if (this.CurrentToken.Kind == SyntaxKind.IdentifierToken && IsCurrentTokenWhereOfConstraintClause()) | ||
{ | ||
identifier = this.AddError(CreateMissingIdentifierToken(), ErrorCode.ERR_IdentifierExpected); |
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.
skipBadParameterListTokens, | ||
allowTrailingSeparator: false, | ||
requireOneElement: false, | ||
requireOneElement: forExtension, // For extension declarations, we require one receiver parameter |
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.
@@ -244,6 +244,7 @@ public static bool IsAnyToken(SyntaxKind kind) | |||
case SyntaxKind.UnderscoreToken: | |||
case SyntaxKind.MultiLineRawStringLiteralToken: | |||
case SyntaxKind.SingleLineRawStringLiteralToken: | |||
case SyntaxKind.ExtensionKeyword: |
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.
Consider covering more error scenarios:
Refers to: src/Compilers/CSharp/Test/Syntax/Parsing/ExtensionsParsingTests.cs:931 in 51686c8. [](commit_id = 51686c8, deletion_comment = False) |
Done with review pass (commit 9) |
I think this may end up allowed, since emitted as static field. In reply to: 2611226090 Refers to: src/Compilers/CSharp/Test/Syntax/Parsing/ExtensionsParsingTests.cs:1713 in 51686c8. [](commit_id = 51686c8, deletion_comment = False) |
The spec doesn't allow fields and we didn't even think what would it mean for implementation. Also, is this going to be an extension field? In reply to: 2611341439 Refers to: src/Compilers/CSharp/Test/Syntax/Parsing/ExtensionsParsingTests.cs:1713 in 51686c8. [](commit_id = 51686c8, deletion_comment = False) |
I suggest sticking to the spec for now In reply to: 2611344424 Refers to: src/Compilers/CSharp/Test/Syntax/Parsing/ExtensionsParsingTests.cs:1713 in 51686c8. [](commit_id = 51686c8, deletion_comment = False) |
This may be allowed (TBD) In reply to: 2611227756 Refers to: src/Compilers/CSharp/Test/Syntax/Parsing/ExtensionsParsingTests.cs:1921 in 51686c8. [](commit_id = 51686c8, deletion_comment = False) |
I suggest sticking to the spec for now In reply to: 2611345773 Refers to: src/Compilers/CSharp/Test/Syntax/Parsing/ExtensionsParsingTests.cs:1921 in 51686c8. [](commit_id = 51686c8, deletion_comment = False) |
Relates to test plan #76130