Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 23 additions & 12 deletions src/Compilers/CSharp/Portable/Parser/LanguageParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6444,8 +6444,10 @@ private StatementSyntax ParseStatementNoDeclaration(bool allowAnyExpression)
case SyntaxKind.GotoKeyword:
return this.ParseGotoStatement();
case SyntaxKind.IfKeyword:
case SyntaxKind.ElseKeyword: // Including 'else' keyword to handle 'else without if' error cases
return this.ParseIfStatement();
case SyntaxKind.ElseKeyword:
// Including 'else' keyword to handle 'else without if' error cases
return this.ParseMisplacedElse();
Copy link
Member

Choose a reason for hiding this comment

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

good idea!

case SyntaxKind.LockKeyword:
return this.ParseLockStatement();
case SyntaxKind.ReturnKeyword:
Expand Down Expand Up @@ -7709,19 +7711,28 @@ private GotoStatementSyntax ParseGotoStatement()

private IfStatementSyntax ParseIfStatement()
{
Debug.Assert(this.CurrentToken.Kind == SyntaxKind.IfKeyword || this.CurrentToken.Kind == SyntaxKind.ElseKeyword);
Debug.Assert(this.CurrentToken.Kind == SyntaxKind.IfKeyword);

bool firstTokenIsElse = this.CurrentToken.Kind == SyntaxKind.ElseKeyword;
var @if = firstTokenIsElse
? this.EatToken(SyntaxKind.IfKeyword, ErrorCode.ERR_ElseCannotStartStatement)
: this.EatToken(SyntaxKind.IfKeyword);
var openParen = this.EatToken(SyntaxKind.OpenParenToken);
var condition = this.ParseExpressionCore();
var closeParen = this.EatToken(SyntaxKind.CloseParenToken);
var statement = firstTokenIsElse ? this.ParseExpressionStatement() : this.ParseEmbeddedStatement();
var elseClause = this.ParseElseClauseOpt();
return _syntaxFactory.IfStatement(
this.EatToken(SyntaxKind.IfKeyword),
this.EatToken(SyntaxKind.OpenParenToken),
this.ParseExpressionCore(),
this.EatToken(SyntaxKind.CloseParenToken),
this.ParseEmbeddedStatement(),
this.ParseElseClauseOpt());
}
Copy link
Member Author

Choose a reason for hiding this comment

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

optimie the fast path of a normal if-statement, with no need for flow control or storage for flow control temps.


private IfStatementSyntax ParseMisplacedElse()
Copy link
Member Author

Choose a reason for hiding this comment

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

MisplacedElse is a very rare case (usually just in the IDE as people are typing). it's fine for it to have it's own dedicated parsing method.

{
Debug.Assert(this.CurrentToken.Kind == SyntaxKind.ElseKeyword);

return _syntaxFactory.IfStatement(@if, openParen, condition, closeParen, statement, elseClause);
return _syntaxFactory.IfStatement(
this.EatToken(SyntaxKind.IfKeyword, ErrorCode.ERR_ElseCannotStartStatement),
this.EatToken(SyntaxKind.OpenParenToken),
Copy link
Member

Choose a reason for hiding this comment

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

Should we call EatToken, ParseExpressionCore, etc in this way even though we know they will always fail? Basically everything before 'ParseElseClauseOpt'?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes. That's the idea (and what the code did before). It effectively synthesizes the missing nodes/tokens we want here

this.ParseExpressionCore(),
this.EatToken(SyntaxKind.CloseParenToken),
this.ParseExpressionStatement(),
this.ParseElseClauseOpt());
}

private ElseClauseSyntax ParseElseClauseOpt()
Expand Down