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

Move to more consistent patterns in the parser. #65724

Merged
merged 52 commits into from
Dec 8, 2022

Conversation

CyrusNajmabadi
Copy link
Member

@CyrusNajmabadi CyrusNajmabadi commented Dec 2, 2022

Definitely review with whitespace off.

if (this.CurrentToken.Kind == SyntaxKind.OperatorKeyword)
{
return null;
}
Copy link
Member Author

Choose a reason for hiding this comment

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

unnecessary. the very next check bails if we don't have an identifiertoken, so if we have an operatorkeyword, that would always bail.


if (result.IsMissing &&
(this.CurrentToken.Kind != SyntaxKind.CommaToken && this.CurrentToken.Kind != SyntaxKind.GreaterThanToken) &&
((nextTokenKind = this.PeekToken(1).Kind) == SyntaxKind.CommaToken || nextTokenKind == SyntaxKind.GreaterThanToken))
(this.PeekToken(1).Kind is SyntaxKind.CommaToken or SyntaxKind.GreaterThanToken))
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 is so much cleaner. locals being assigned halfway through an expr to read later is not good :)

@@ -7227,6 +7197,7 @@ private SyntaxToken EatNullableQualifierIfApplicable(ParseTypeMode mode)
Debug.Assert(this.CurrentToken.Kind == SyntaxKind.QuestionToken);
using var resetPoint = this.GetDisposableResetPoint(resetOnDispose: false);


Copy link
Member Author

Choose a reason for hiding this comment

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

Suggested change

@@ -10945,29 +10911,31 @@ private ExpressionSyntax ParseTermWithoutPostfix(Precedence precedence)
return this.AddError(this.CreateMissingIdentifierName(), ErrorCode.ERR_InvalidExprTerm, this.CurrentToken.Text);
}
case SyntaxKind.IdentifierToken:
if (this.IsTrueIdentifier())
{
Copy link
Member Author

@CyrusNajmabadi CyrusNajmabadi Dec 2, 2022

Choose a reason for hiding this comment

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

braces are to prevent some conflicts with pattern name clashes in a switch statement.

@CyrusNajmabadi CyrusNajmabadi marked this pull request as ready for review December 2, 2022 23:29
@CyrusNajmabadi CyrusNajmabadi requested a review from a team as a code owner December 2, 2022 23:29
@CyrusNajmabadi
Copy link
Member Author

@jjonescz @RikkiGibson @333fred ptal :)

@@ -4774,7 +4693,7 @@ private void ParseParameterModifiers(SyntaxListBuilder modifiers, bool isFunctio
this.ParseVariableDeclarators(type, flags: VariableFlags.LocalOrField, variables: variables, parentKind: parentKind);

// Make 'scoped' part of the type when it is the last token in the modifiers list
if (modifiers.Count != 0 && modifiers[modifiers.Count - 1] is SyntaxToken { Kind: SyntaxKind.ScopedKeyword } scopedKeyword)
if (modifiers is [.., SyntaxToken { Kind: SyntaxKind.ScopedKeyword } scopedKeyword])
Copy link
Member Author

Choose a reason for hiding this comment

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

Yaay patterns.

Copy link
Member

@333fred 333fred left a comment

Choose a reason for hiding this comment

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

Done review pass.

src/Compilers/CSharp/Portable/Parser/LanguageParser.cs Outdated Show resolved Hide resolved
@@ -7111,8 +6995,11 @@ bool canBeNullableType()
if (mode == ParseTypeMode.DefinitePattern)
return true; // Permit nullable type parsing and report while binding for a better error message
if (mode == ParseTypeMode.NewExpression && type.Kind == SyntaxKind.TupleType &&
this.PeekToken(1).Kind != SyntaxKind.OpenParenToken && this.PeekToken(1).Kind != SyntaxKind.OpenBraceToken)
this.PeekToken(1).Kind is not SyntaxKind.OpenParenToken and not SyntaxKind.OpenBraceToken)
{
Copy link
Member

Choose a reason for hiding this comment

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

Most of the file you've removed these, but here you added them?

Copy link
Member Author

Choose a reason for hiding this comment

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

so thsi is a requirement of .net style/formatting. you can only have a single-line body without braces if the condition is also single-line. i can remove, but i figured while i was here i should address it.

}

return _syntaxFactory.JoinClause(@join, type, name, @in, inExpression, @on, leftExpression, @equals, rightExpression, joinInto);
return _syntaxFactory.JoinClause(
Copy link
Member

Choose a reason for hiding this comment

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

I would prefer these using named parameters please, reading this is much more difficult than the original version with local names.

Copy link
Member Author

Choose a reason for hiding this comment

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

will do!

Copy link
Member Author

Choose a reason for hiding this comment

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

done.

@CyrusNajmabadi
Copy link
Member Author

hey @RikkiGibson could i get eyes on this?

@RikkiGibson RikkiGibson self-assigned this Dec 8, 2022
token = this.AddError(token, ErrorCode.ERR_MemberNeedsType);
var voidType = _syntaxFactory.PredefinedType(token);
var voidType = _syntaxFactory.PredefinedType(
this.AddError(SyntaxFactory.MissingToken(SyntaxKind.VoidKeyword), ErrorCode.ERR_MemberNeedsType));
Copy link
Contributor

Choose a reason for hiding this comment

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

I am wondering if a syntactically simpler helper for creating a missing token with a certain error on it would be desirable here and in many other places. For example: this.CreateMissingToken(SyntaxKind.VoidKeyword, ErrorCode.MemberNeedsType)

Copy link
Member Author

Choose a reason for hiding this comment

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

that would make a ton of sense, and i would def be in support of helpers for common patterns like this.

@@ -6270,7 +6167,7 @@ private bool IsOpenName()
separator = ConvertToMissingWithTrailingTrivia(separator, SyntaxKind.DotToken);
}

if (isEvent && this.CurrentToken.Kind != SyntaxKind.OpenBraceToken && this.CurrentToken.Kind != SyntaxKind.SemicolonToken)
if (isEvent && this.CurrentToken.Kind is not SyntaxKind.OpenBraceToken and not SyntaxKind.SemicolonToken)
Copy link
Contributor

Choose a reason for hiding this comment

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

I wouldn't mind seeing these De Morgan'd like this.CurrentToken.Kind is not (SyntaxKind.OpenBraceToken or SyntaxKind.SemicolonToken)

Copy link
Member Author

Choose a reason for hiding this comment

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

i was on the fence. Parenthesized patterns don't quite feel natural to me just yet. And i thought this was nice for preserving the same exact type of check we previously had. e.g. it was && and != before, so makign that and and not seemed the least contentious.

this.CurrentToken.Kind == SyntaxKind.OpenBracketToken || // array type
this.CurrentToken.Kind == SyntaxKind.OpenBraceToken; // object initializer
this.CurrentToken.Kind is SyntaxKind.OpenParenToken or // ctor parameters
SyntaxKind.OpenBracketToken or // array type
Copy link
Contributor

Choose a reason for hiding this comment

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

I personally would not do this (inserting extra spaces in order to make certain tokens align across lines).

In fact, this is something the format analyzer forbids in other cases. e.g.

[Flags]
private enum Modifiers
{
#pragma warning disable format
None = 0,
Static = 1 << 0,
Abstract = 1 << 1,
New = 1 << 2,
Unsafe = 1 << 3,
ReadOnly = 1 << 4,
Virtual = 1 << 5,
Override = 1 << 6,
Sealed = 1 << 7,
Const = 1 << 8,
WithEvents = 1 << 9,
Partial = 1 << 10,
Async = 1 << 11,
WriteOnly = 1 << 12,
Ref = 1 << 13,
Volatile = 1 << 14,
Extern = 1 << 15,
Required = 1 << 16,
File = 1 << 17,
#pragma warning restore format
}

I would much rather see us simply insert a line break + indent before the token where we desire the alignment.

return this.CurrentToken.Kind is
    SyntaxKind.OpenParenToken or
    SyntaxKind.OpenBracketToken or
    SyntaxKind.OpenBraceToken;

I prefer even more to put the operator token at the start of the line. SyntaxKind will not strictly align in this case (if we used an and or not operator in some places, for example), but the visual pop of seeing the smaller operator token at the start of the line seems better for skimmability, in my opinion.

return this.CurrentToken.Kind
    is SyntaxKind.OpenParenToken
    or SyntaxKind.OpenBracketToken
    or SyntaxKind.OpenBraceToken;

Copy link
Member Author

Choose a reason for hiding this comment

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

i can do that. i checked with IDE at one point if we had a view on where was best for operators, and currently there's no consistency from the team. so we'ved decided to not be opinionated there.


case SyntaxKind.ExclamationToken:
expr = _syntaxFactory.PostfixUnaryExpression(SyntaxKind.SuppressNullableWarningExpression, expr, this.EatToken());
break;
continue;
Copy link
Contributor

Choose a reason for hiding this comment

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

It looks like using continue here instead of break is just to improve clarity? We used to jump to the end of the switch statement and then hit the top of the next loop iteration, but now we just hit the top of the next loop iteration?

Copy link
Member Author

Choose a reason for hiding this comment

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

correct. IMO 'continue' is better for clarity. 'break' both has the issue of being confusing (am i breaking the loop? or am i in a switch?), and needing the reader to then need to see what happens at the end of hte loop. 'continue' perfect describes what is happening, and doesn't require forward reading.

this.EatTokenAsKind(SyntaxKind.IdentifierToken) :
this.ParseIdentifierToken();
var identifier = (this.CurrentToken.Kind != SyntaxKind.IdentifierToken && this.PeekToken(1).Kind == SyntaxKind.EqualsGreaterThanToken)
? this.EatTokenAsKind(SyntaxKind.IdentifierToken)
Copy link
Contributor

Choose a reason for hiding this comment

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

I personally find it strange that we like to put the ?: tokens at the start of the line, but we like to put the is/or tokens at the end of the line.

Copy link
Member Author

Choose a reason for hiding this comment

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

Jah. This is the opinionated IDE style we have. We have a formatting rule for this as well, but it (like most things) is turned off in teh compiler layer :)

@CyrusNajmabadi CyrusNajmabadi merged commit dabbd10 into dotnet:main Dec 8, 2022
@CyrusNajmabadi CyrusNajmabadi deleted the parserCleanup branch December 8, 2022 21:54
@ghost ghost added this to the Next milestone Dec 8, 2022
@Cosifne Cosifne modified the milestones: Next, 17.5 P3 Jan 4, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants