-
Notifications
You must be signed in to change notification settings - Fork 4k
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
Initial support for collection literal parsing. #66417
Initial support for collection literal parsing. #66417
Conversation
if (isPossibleAttributeOrModifier) | ||
var isPossibleModifier = IsAdditionalLocalFunctionModifier(tk) | ||
&& (tk is not (SyntaxKind.AsyncKeyword or SyntaxKind.ScopedKeyword) | ||
|| ShouldContextualKeywordBeTreatedAsModifier(parsingStatementNotDeclaration: 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.
we don't need to check for attributes here since attributes have already been parsed out before this.
@@ -527,7 +527,7 @@ private enum NamespaceParts | |||
// incomplete members must be processed before we add any nodes to the body: | |||
ReduceIncompleteMembers(ref pendingIncompleteMembers, ref openBraceOrSemicolon, ref body, ref initialBadNodes); | |||
|
|||
var attribute = this.ParseAttributeDeclaration(); | |||
var attribute = this.ParseAttributeDeclaration(inExpressionContext: parentKind == SyntaxKind.CompilationUnit); |
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.
[
at the top level could start an attribute, or could start a collection literal. However, [
inside a namespace always starts an attribute. Good test cases @jnm2 :)
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.
while not legal, the parser should also accept:
[IAmAnAttribute(1, 2, 3)]
[on, a, collection, literal].DoWork();
@@ -2776,7 +2793,7 @@ private MemberDeclarationSyntax ParseMemberDeclarationCore(SyntaxKind parentKind | |||
|
|||
try | |||
{ | |||
var attributes = this.ParseAttributeDeclarations(); | |||
var attributes = this.ParseAttributeDeclarations(inExpressionContext: false); |
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.
all the places taht pass in false should have tests taht show we still think tehy are an attribute, even if the user types .
after them.
|
||
// PROTOTYPE: Def back compat concern here. What if the user has `x ? y?[0] : z` this would be legal, | ||
// but will now change to `y?[0] : z` being a ternary. Have to decide if this is acceptable break. | ||
return this.CurrentToken.Kind != SyntaxKind.ColonToken; |
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.
some interesting compat cases. we may have to be strict and require the user to write x ? ([1, 2, 3]) : ([4, 5, 6])
. But that seems Really unfortunate. So i'm currently allowing the nicer form of x ? [1, 2, 3] : [4, 5, 6]
, but it may cause a compat break that LDM will have to approve.
|
||
if (this.CurrentToken.Kind != SyntaxKind.CloseBracketToken) | ||
{ | ||
tryAgain: |
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 form is how we always do separated syntax list parsing. I personally dont' like repeating the entire full form. but it's what we do everywhere wlse, so this is consistent.
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.
Is this referring to the duplicated IsPossibleCollectionCreationExpression() || this.CurrentToken.Kind == SyntaxKind.CommaToken
check, before and within the loop? It looks like there's also a duplicated SkipBadCollectionCreationExpressionTokens()
check within the loop and following the loop.
If it's simpler to combine the duplicated checks in each case, within a while (true) { ... }
for instance, I'd prefer it.
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.
I would very much like to keep this form in sync with all the other ParseSeparatedList algorithms we have in the parser. if we do want to change them, doign them all at once would be my preference.
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.
I also really hate this code, and fwiw we are already not consistent on separated syntax list parsing algorithms. ParseFunctionPointerType
, for example, uses a much simpler form of this.
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.
Funny, I was just noting in #65256 (comment) that switch expression arm parsing does it its own way too :)
I would like for goto tryAgain;
to goto the git history and for us to do something uniformly better here. But that seems like work better done in the base branch.
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.
I think a very suitable approach would be to have a basic ParseSeparatedList helper which takes in a few callbacks (And some boolean options, like "allowTrailingSeparator"). Using your approach of passing the parser instance along rikki, we could ensure this doesn't cause allocs.
But i was worried about potential perf regressions of doing that, so i wanted to leave to an alternate PR that could be tested in isolation.
|
||
var expression = this.ParseExpression(); | ||
if (dotDotToken != null) | ||
return _syntaxFactory.SpreadElement(dotDotToken, expression); |
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.
interesting case woudl be ..
without a following expression. .. ..goo
. goo:
(without a following expression). And things like [x ? 1 : 2 : value]
(which i believe the spec will not allow... but i think will be allowed here).
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.
also: [ : value]
(Without a key).
…jmabadi/roslyn into collectionLiteralParsing
…jmabadi/roslyn into collectionLiteralParsing
@jcouv could you take a look? |
nit: consider file-scoped namespace In reply to: 1410787932 In reply to: 1410787932 Refers to: src/Compilers/CSharp/Test/Syntax/Parsing/CollectionLiteralParsingTests.cs:9 in 3db96f8. [](commit_id = 3db96f8, deletion_comment = False) |
Done. |
@@ -10103,6 +10150,7 @@ private static Precedence GetPrecedence(SyntaxKind op) | |||
case SyntaxKind.ArrayCreationExpression: | |||
case SyntaxKind.BaseExpression: | |||
case SyntaxKind.CharacterLiteralExpression: | |||
case SyntaxKind.CollectionCreationExpression: |
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: please mention the precedence (and the resolution on any upcoming ambiguity decision) in the spec. Rex/Bill likely to ask question on that when they get to standardization. #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.
I can def add more. But it's already in the spec with where this construct is in the grammar. (can't hurt to have more info though of course) :)
{ | ||
return @this.SkipBadSeparatedListTokensWithExpectedKind(ref openBracket, list, | ||
static p => p.CurrentToken.Kind != SyntaxKind.CommaToken && !p.IsPossibleCollectionElement(), | ||
static (p, closeKind) => p.CurrentToken.Kind == closeKind, |
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.
The shadowing of closeKind
is confusing. Let's use different identifiers.
I see that we did that previously in SkipBadInitializerListTokens
:-/ Still wouldn't recommend it. #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.
this shadowing is extremely intentional (and done with all callback lambdas). It's precisely to indicate taht it's the same value, just passed through the lambda as an arg so that you always have hte same gy. Renaming is actually more confusing here as now there's the potential semantic confusion of "oh wait... is this closeKind2 somehow different from the containing closeKind1, and what does that mean?"
As with teh above, if we are to change this, i woulid prefer it be in a separate PR that does that for all the code like this (there are at least a dozen places to fixup if we do this).
if (SyntaxFacts.IsReservedKeyword(this.CurrentToken.Kind) && this.PeekToken(1).Kind == SyntaxKind.ColonToken) | ||
{ | ||
var keyword = this.EatTokenWithPrejudice(SyntaxKind.IdentifierToken); | ||
var identifier = ConvertToIdentifier(keyword).WithDiagnosticsGreen(keyword.GetDiagnostics()); |
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.
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. ConvertToIdentifier doesn't preserve diagnostics.
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.
Can there be diagnostics? Other places that use ConvertToIdentifier
don't do this
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.
I don't think diagnostics can occur. But if they could, then the WithDiagnosticsGreen
call should be inside ConvertToIdentifier
(like we do in ConvertToKeyword
), not the responsibility of the caller of ConvertToIdentifier
.
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.
Can there be diagnostics?
Yes. EatTokenWithPrejudice adds one.
Other places that use ConvertToIdentifier don't do this
They likely aren't using EatTokenWithPrejudice :)
But if they could, then the WithDiagnosticsGreen call should be inside ConvertToIdentifier
I can do that and see what happens.
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.
Done with review pass (iteration 65)
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.
LGTM Thanks! (iteration 66)
I'll summon the Gandalf in me to remind you that this shall be squashed 🧙
Thanks! :-) |
@@ -15,6 +15,7 @@ | |||
|
|||
namespace Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax | |||
{ | |||
using Microsoft.CodeAnalysis.CSharp.Symbols; |
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.
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.
nope. sorry :( can remove in followup.
Followup to #66412
Relates to test plan #66418