-
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
Improve error recovery when encountering errant close-paren tokens. #69482
Improve error recovery when encountering errant close-paren tokens. #69482
Conversation
@@ -8406,7 +8412,7 @@ private FixedStatementSyntax ParseFixedStatement(SyntaxList<AttributeListSyntax> | |||
|
|||
var saveTerm = _termState; | |||
_termState |= TerminatorState.IsEndOfFixedStatement; | |||
var decl = ParseVariableDeclaration(); |
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.
renamed this to make it clear which caller contexts it is expected to be used in. The old name was highly misleading and could make people think they should they should use this in any circumstance they want to parse a variable-declaration (like in a field for example).
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.
importantly, all of these are the constructs that do want to stop parsing the variable declaration once they hit an errant close paren, as their construct themselves want to consume the close paren into themselves.
else if (stopOnCloseParen && this.CurrentToken.Kind == SyntaxKind.CloseParenToken) | ||
{ | ||
break; | ||
} |
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 half of the fix. Callers who are parsing a list of declarators say how close parens should be treated.
@@ -9784,7 +9800,6 @@ private bool IsEndOfDeclarationClause() | |||
switch (this.CurrentToken.Kind) | |||
{ | |||
case SyntaxKind.SemicolonToken: | |||
case SyntaxKind.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.
this is the other half. we don't mix 'close paren' into the 'bail out set' for error recovery.
@@ -2034,33 +2034,21 @@ public void StaticLambdaFunctionPointer() | |||
Diagnostic(ErrorCode.ERR_SemicolonExpected, "{").WithLocation(1, 58)); |
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.
view with whitespace off.
…/roslyn into closeParenParsing
// (1,32): error CS1002: ; expected | ||
// delegate*<void> ptr = &static () => { }; | ||
Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(1, 32)); | ||
Diagnostic(ErrorCode.ERR_SyntaxError, "static").WithArguments(",").WithLocation(1, 24)); |
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.
virtually all cases got better. with many fewer error recovery errors than before.
@@ -2103,7 +2091,7 @@ void verify() | |||
} | |||
} | |||
} | |||
M(SyntaxKind.SemicolonToken); | |||
N(SyntaxKind.SemicolonToken); |
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.
Lots less missing tokens as error recovery now does a better job not preemptively bailing out.
@dotnet/roslyn-compiler ptal. This helps a lot of situations where a simple errant close paren can cause huge cascading failures in parsing. |
@RikkiGibson @cston ptal. |
@@ -4528,22 +4528,11 @@ private void ParseParameterModifiers(SyntaxListBuilder modifiers, bool isFunctio | |||
|
|||
var type = this.ParseType(); | |||
|
|||
var saveTerm = _termState; | |||
_termState |= TerminatorState.IsEndOfFieldDeclaration; |
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.
moved this into the single ParseFieldDeclarationVariableDeclarators helper.
var variables = _pool.AllocateSeparated<VariableDeclaratorSyntax>(); | ||
try | ||
{ | ||
this.ParseVariableDeclarators(type, VariableFlags.Fixed, variables, parentKind); |
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.
renamed ParseVariableDeclarators to ParseFieldDeclarationVariableDeclarators since that's the only case it is used for. this allowed a lot of simplification.
// (1,18): error CS1022: Type or namespace definition, or end-of-file expected | ||
// [return: A, B].C(); | ||
Diagnostic(ErrorCode.ERR_EOFExpected, ")").WithLocation(1, 18)); | ||
Diagnostic(ErrorCode.ERR_SyntaxError, "]").WithArguments(",").WithLocation(1, 14)); |
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 recover much faster in practically all cases now. no cases get worse. a small handful stay around the same.
N(SyntaxKind.GlobalStatement); | ||
{ | ||
N(SyntaxKind.EmptyStatement); | ||
{ | ||
N(SyntaxKind.SemicolonToken); |
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.
nothing like thinking you're missing a semicolon, only to be immediatley followed by an unmatched semicolon that makes its own statement.
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.
it's nice to see missing tokens "restored" and a cleaner parse.
{ | ||
N(SyntaxKind.IdentifierToken, "M"); | ||
} | ||
} |
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.
old code basically tried to guess what was going on after the )
, getting is quite wrong. new paths basically go into error recovery and state that we can't handle things until we get to the next semicolon.
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 also means a lot less broken constructs and a lot less error messages in these bogus cases.
@RikkiGibson @cston ptal. |
src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs
Show resolved
Hide resolved
N(SyntaxKind.GlobalStatement); | ||
{ | ||
N(SyntaxKind.EmptyStatement); | ||
{ | ||
N(SyntaxKind.SemicolonToken); |
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.
it's nice to see missing tokens "restored" and a cleaner parse.
Co-authored-by: Rikki Gibson <[email protected]>
_syntaxFactory.VariableDeclaration(type, _pool.ToListAndFree(variables)), | ||
_syntaxFactory.VariableDeclaration( | ||
type, | ||
this.ParseFieldDeclarationVariableDeclarators(type, VariableFlags.Const, parentKind)), |
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.
honestly, it's likely better there so that parsing a constant field is the same as parsing a normal field. TBH, it's kinda strange that we even have this method instead of just consuming 'const' in the caller and calling into normal field decl parsing.
The only real difference here is that ;
is mixed into the terminator tokens for this construct. but that's totally fine and desirable for constant fields.
Fixes #58529
Our recursive parsing system was errantly adding close-paren tokens into the set of tokens used to determine when to bail out of a scope. This is not an appropriate use of that system as it should only look for tokens known to absolutely terminate a construct, or tokens known to absolutely start another sibling or parent construct.
Fixed this to no longer be part of the error recovery system, and instead just treat the close-paren specially for the 3 parsing constructs that cared about it.
Note: the majority of this PR is deletions. THe addition of a large test though makes it seem otherwise.