Skip to content

Commit f20ab1d

Browse files
Improve error recovery for => in constructor initializers (#80764)
Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: CyrusNajmabadi <[email protected]> Co-authored-by: Cyrus Najmabadi <[email protected]>
1 parent 3ea5c1c commit f20ab1d

File tree

2 files changed

+880
-23
lines changed

2 files changed

+880
-23
lines changed

src/Compilers/CSharp/Portable/Parser/LanguageParser.cs

Lines changed: 33 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3362,6 +3362,8 @@ public static bool IsComplete(CSharpSyntaxNode node)
33623362
return true;
33633363
}
33643364

3365+
#nullable enable
3366+
33653367
private ConstructorDeclarationSyntax ParseConstructorDeclaration(
33663368
SyntaxList<AttributeListSyntax> attributes, SyntaxListBuilder modifiers)
33673369
{
@@ -3371,9 +3373,7 @@ private ConstructorDeclarationSyntax ParseConstructorDeclaration(
33713373
try
33723374
{
33733375
var paramList = this.ParseParenthesizedParameterList(forExtension: false);
3374-
var initializer = this.CurrentToken.Kind == SyntaxKind.ColonToken
3375-
? this.ParseConstructorInitializer()
3376-
: null;
3376+
var initializer = this.TryParseConstructorInitializer();
33773377

33783378
this.ParseBlockAndExpressionBodiesWithSemicolon(out var body, out var expressionBody, out var semicolon);
33793379

@@ -3385,38 +3385,48 @@ private ConstructorDeclarationSyntax ParseConstructorDeclaration(
33853385
}
33863386
}
33873387

3388-
private ConstructorInitializerSyntax ParseConstructorInitializer()
3388+
private ConstructorInitializerSyntax? TryParseConstructorInitializer()
33893389
{
3390-
var colon = this.EatToken(SyntaxKind.ColonToken);
3390+
var currentTokenKind = this.CurrentToken.Kind;
3391+
var shouldParse = currentTokenKind is SyntaxKind.ColonToken ||
3392+
(currentTokenKind is SyntaxKind.EqualsGreaterThanToken &&
3393+
this.PeekToken(1).Kind is SyntaxKind.ThisKeyword or SyntaxKind.BaseKeyword &&
3394+
this.PeekToken(2).Kind is SyntaxKind.OpenParenToken);
33913395

3392-
var reportError = true;
3393-
var kind = this.CurrentToken.Kind == SyntaxKind.BaseKeyword
3394-
? SyntaxKind.BaseConstructorInitializer
3395-
: SyntaxKind.ThisConstructorInitializer;
3396+
if (!shouldParse)
3397+
return null;
33963398

3397-
SyntaxToken token;
3398-
if (this.CurrentToken.Kind is SyntaxKind.BaseKeyword or SyntaxKind.ThisKeyword)
3399-
{
3400-
token = this.EatToken();
3401-
}
3402-
else
3403-
{
3404-
token = this.EatToken(SyntaxKind.ThisKeyword, ErrorCode.ERR_ThisOrBaseExpected);
3399+
return ParseConstructorInitializer();
3400+
}
34053401

3406-
// No need to report further errors at this point:
3407-
reportError = false;
3408-
}
3402+
private ConstructorInitializerSyntax ParseConstructorInitializer()
3403+
{
3404+
// Normally called for `:` but also in some error recovery circumstances for `=>`. EatTokenAsKind handles
3405+
// both cases properly, producing the right errors we need in the latter case, and always consuming
3406+
// whichever token we're coming into this method on.
3407+
Debug.Assert(this.CurrentToken.Kind is SyntaxKind.ColonToken or SyntaxKind.EqualsGreaterThanToken);
3408+
var colon = this.EatTokenAsKind(SyntaxKind.ColonToken);
3409+
3410+
var token = this.CurrentToken.Kind is SyntaxKind.BaseKeyword or SyntaxKind.ThisKeyword
3411+
? this.EatToken()
3412+
: this.EatToken(SyntaxKind.ThisKeyword, ErrorCode.ERR_ThisOrBaseExpected);
34093413

34103414
var argumentList = this.CurrentToken.Kind == SyntaxKind.OpenParenToken
34113415
? this.ParseParenthesizedArgumentList()
34123416
: _syntaxFactory.ArgumentList(
3413-
this.EatToken(SyntaxKind.OpenParenToken, reportError),
3417+
this.EatToken(SyntaxKind.OpenParenToken, reportError: !token.ContainsDiagnostics),
34143418
arguments: default,
3415-
this.EatToken(SyntaxKind.CloseParenToken, reportError));
3419+
this.EatToken(SyntaxKind.CloseParenToken, reportError: !token.ContainsDiagnostics));
34163420

3417-
return _syntaxFactory.ConstructorInitializer(kind, colon, token, argumentList);
3421+
return _syntaxFactory.ConstructorInitializer(
3422+
token.Kind == SyntaxKind.BaseKeyword
3423+
? SyntaxKind.BaseConstructorInitializer
3424+
: SyntaxKind.ThisConstructorInitializer,
3425+
colon, token, argumentList);
34183426
}
34193427

3428+
#nullable disable
3429+
34203430
private DestructorDeclarationSyntax ParseDestructorDeclaration(SyntaxList<AttributeListSyntax> attributes, SyntaxListBuilder modifiers)
34213431
{
34223432
Debug.Assert(this.CurrentToken.Kind == SyntaxKind.TildeToken);

0 commit comments

Comments
 (0)