未验证 提交 cbc87518 编写于 作者: M msftbot[bot] 提交者: GitHub

Merge pull request #41556 from CyrusNajmabadi/awaitErrorRecovery

Simplify error recovery for misplaced await-expressions.
......@@ -6319,7 +6319,7 @@ private StatementSyntax ParseStatementCore()
// it as either a declaration or as an "await X();" statement that is in a non-async
// method.
return ParsePossibleDeclarationOrBadAwaitStatement();
return TryParsePossibleDeclarationOrBadAwaitStatement();
}
finally
{
......@@ -6327,87 +6327,64 @@ private StatementSyntax ParseStatementCore()
}
}
private StatementSyntax ParsePossibleDeclarationOrBadAwaitStatement()
private StatementSyntax TryParsePossibleDeclarationOrBadAwaitStatement()
{
ResetPoint resetPointBeforeStatement = this.GetResetPoint();
try
{
StatementSyntax result = ParsePossibleDeclarationOrBadAwaitStatement(ref resetPointBeforeStatement);
return result;
}
finally
{
this.Release(ref resetPointBeforeStatement);
}
}
private StatementSyntax ParsePossibleDeclarationOrBadAwaitStatement(ref ResetPoint resetPointBeforeStatement)
{
// Precondition: We have already attempted to parse the statement as a non-declaration and failed.
//
// That means that we are in one of the following cases:
//
// 1) This is not a statement. This can happen if the start of the statement was an
// accessibility modifier, but the rest of the statement did not parse as a local
// function. If there was an accessibility modifier and the statement parsed as
// local function, that should be marked as a mistake with local function visibility.
// Otherwise, it's likely the user just forgot a closing brace on their method.
// 2) This is a perfectly mundane and correct local declaration statement like "int x;"
// 3) This is a perfectly mundane but erroneous local declaration statement, like "int X();"
// 4) We are in the rare case of the code containing "await x;" and the intention is that
// "await" is the type of "x". This only works in a non-async method.
// 5) We have what would be a legal await statement, like "await X();", but we are not in
// an async method, so the parse failed. (Had we been in an async method then the parse
// attempt done by our caller would have succeeded.)
// 6) The statement begins with "await" but is not a legal local declaration and not a legal
// await expression regardless of whether the method is marked as "async".
bool beginsWithAwait = this.CurrentToken.ContextualKind == SyntaxKind.AwaitKeyword;
StatementSyntax result = ParseLocalDeclarationStatement();
// Case (1)
if (result == null)
{
this.Reset(ref resetPointBeforeStatement);
return null;
}
// Precondition: We have already attempted to parse the statement as a non-declaration and failed.
//
// That means that we are in one of the following cases:
//
// 1) This is not a statement. This can happen if the start of the statement was an
// accessibility modifier, but the rest of the statement did not parse as a local
// function. If there was an accessibility modifier and the statement parsed as
// local function, that should be marked as a mistake with local function visibility.
// Otherwise, it's likely the user just forgot a closing brace on their method.
// 2) This is a perfectly mundane and correct local declaration statement like "int x;"
// 3) This is a perfectly mundane but erroneous local declaration statement, like "int X();"
// 4) We are in the rare case of the code containing "await x;" and the intention is that
// "await" is the type of "x". This only works in a non-async method.
// 5) We have a misplaced await statement in a non-async method, like "await X();",
// so the parse failed. Had we been in an async method then the parse attempt
// done by our caller would have succeeded. Retry as if we were async. Later
// semantic code will error out that this isn't legal.
bool beginsWithAwait = this.CurrentToken.ContextualKind == SyntaxKind.AwaitKeyword;
StatementSyntax result = ParseLocalDeclarationStatement();
// Case (1)
if (result == null)
{
this.Reset(ref resetPointBeforeStatement);
return null;
}
// Cases (2), (3) and (4):
if (!beginsWithAwait || !result.ContainsDiagnostics)
{
return result;
}
// Cases (2), (3) and (4):
if (!beginsWithAwait || !result.ContainsDiagnostics)
{
return result;
}
// The statement begins with "await" and could not be parsed as a legal declaration statement.
// We know from our precondition that it is not a legal "await X();" statement, though it is
// possible that it was only not legal because we were not in an async context.
// The statement begins with "await" and could not be parsed as a legal declaration statement.
// We know from our precondition that it is not a legal "await X();" statement, though it is
// possible that it was only not legal because we were not in an async context.
Debug.Assert(!IsInAsync);
Debug.Assert(!IsInAsync);
// Let's see if we're in case (5). Pretend that we're in an async method and see if parsing
// a non-declaration statement would have succeeded.
// Let's see if we're in case (5). Pretend that we're in an async method and retry.
this.Reset(ref resetPointBeforeStatement);
IsInAsync = true;
result = ParseStatementNoDeclaration(allowAnyExpression: false);
IsInAsync = false;
this.Reset(ref resetPointBeforeStatement);
IsInAsync = true;
result = ParseStatementNoDeclaration(allowAnyExpression: false);
IsInAsync = false;
if (!result.ContainsDiagnostics)
{
// We are in case (5). We do not report that we have an "await" expression in a non-async
// method at parse time; rather we do that in BindAwait(), during the initial round of
// semantic analysis.
return result;
}
// We are in case (6); we can't figure out what is going on here. Our best guess is that it is
// a malformed local declaration, so back up and re-parse it.
this.Reset(ref resetPointBeforeStatement);
result = ParseLocalDeclarationStatement();
Debug.Assert(result.ContainsDiagnostics);
return result;
finally
{
this.Release(ref resetPointBeforeStatement);
}
}
/// <summary>
......
......@@ -1890,28 +1890,21 @@ public void BadLocalDeclarationAndAwaitExpressionInSyncContext()
N(SyntaxKind.Block);
{
N(SyntaxKind.OpenBraceToken);
N(SyntaxKind.LocalDeclarationStatement);
N(SyntaxKind.ExpressionStatement);
{
N(SyntaxKind.VariableDeclaration);
N(SyntaxKind.AwaitExpression);
{
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "await");
}
N(SyntaxKind.VariableDeclarator);
N(SyntaxKind.AwaitKeyword);
N(SyntaxKind.InvocationExpression);
{
N(SyntaxKind.IdentifierToken, "goo");
N(SyntaxKind.BracketedArgumentList);
N(SyntaxKind.IdentifierName);
{
M(SyntaxKind.OpenBracketToken);
M(SyntaxKind.Argument);
{
M(SyntaxKind.IdentifierName);
{
M(SyntaxKind.IdentifierToken);
}
}
N(SyntaxKind.CloseBracketToken);
N(SyntaxKind.IdentifierToken, "goo");
}
N(SyntaxKind.ArgumentList);
{
N(SyntaxKind.OpenParenToken);
M(SyntaxKind.CloseParenToken);
}
}
}
......@@ -1943,28 +1936,21 @@ public void BadStatementInSyncContext()
N(SyntaxKind.Block);
{
N(SyntaxKind.OpenBraceToken);
N(SyntaxKind.LocalDeclarationStatement);
N(SyntaxKind.ExpressionStatement);
{
N(SyntaxKind.VariableDeclaration);
N(SyntaxKind.AwaitExpression);
{
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "await");
}
N(SyntaxKind.VariableDeclarator);
N(SyntaxKind.AwaitKeyword);
N(SyntaxKind.InvocationExpression);
{
N(SyntaxKind.IdentifierToken, "goo");
N(SyntaxKind.BracketedArgumentList);
N(SyntaxKind.IdentifierName);
{
M(SyntaxKind.OpenBracketToken);
M(SyntaxKind.Argument);
{
M(SyntaxKind.IdentifierName);
{
M(SyntaxKind.IdentifierToken);
}
}
N(SyntaxKind.CloseBracketToken);
N(SyntaxKind.IdentifierToken, "goo");
}
N(SyntaxKind.ArgumentList);
{
N(SyntaxKind.OpenParenToken);
M(SyntaxKind.CloseParenToken);
}
}
}
......
......@@ -71,28 +71,17 @@ void M6()
N(SyntaxKind.Block);
{
N(SyntaxKind.OpenBraceToken);
N(SyntaxKind.LocalDeclarationStatement);
{
N(SyntaxKind.VariableDeclaration);
{
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "await");
}
N(SyntaxKind.VariableDeclarator);
{
N(SyntaxKind.IdentifierToken, "L");
}
}
M(SyntaxKind.SemicolonToken);
}
N(SyntaxKind.ExpressionStatement);
{
N(SyntaxKind.LessThanExpression);
{
M(SyntaxKind.IdentifierName);
N(SyntaxKind.AwaitExpression);
{
M(SyntaxKind.IdentifierToken);
N(SyntaxKind.AwaitKeyword);
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "L");
}
}
N(SyntaxKind.LessThanToken);
M(SyntaxKind.IdentifierName);
......@@ -206,21 +195,21 @@ void M6()
N(SyntaxKind.Block);
{
N(SyntaxKind.OpenBraceToken);
N(SyntaxKind.LocalDeclarationStatement);
N(SyntaxKind.ExpressionStatement);
{
N(SyntaxKind.VariableDeclaration);
N(SyntaxKind.AwaitExpression);
{
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "await");
}
N(SyntaxKind.VariableDeclarator);
N(SyntaxKind.AwaitKeyword);
N(SyntaxKind.InvocationExpression);
{
N(SyntaxKind.IdentifierToken, "L");
M(SyntaxKind.BracketedArgumentList);
N(SyntaxKind.IdentifierName);
{
M(SyntaxKind.OpenBracketToken);
M(SyntaxKind.CloseBracketToken);
N(SyntaxKind.IdentifierToken, "L");
}
N(SyntaxKind.ArgumentList);
{
N(SyntaxKind.OpenParenToken);
M(SyntaxKind.CloseParenToken);
}
}
}
......
......@@ -2752,25 +2752,25 @@ async void M()
[Fact]
public void TestAwaitUsingVarWithVarAndNoUsingDeclarationTree()
{
UsingStatement(@"await var a = b;", TestOptions.Regular8, expectedErrors:
// (1,11): error CS1003: Syntax error, ',' expected
UsingStatement(@"await var a = b;", TestOptions.Regular8,
// (1,1): error CS1073: Unexpected token 'a'
// await var a = b;
Diagnostic(ErrorCode.ERR_SyntaxError, "a").WithArguments(",", "").WithLocation(1, 11)
);
N(SyntaxKind.LocalDeclarationStatement);
Diagnostic(ErrorCode.ERR_UnexpectedToken, "await var ").WithArguments("a").WithLocation(1, 1),
// (1,11): error CS1002: ; expected
// await var a = b;
Diagnostic(ErrorCode.ERR_SemicolonExpected, "a").WithLocation(1, 11));
N(SyntaxKind.ExpressionStatement);
{
N(SyntaxKind.VariableDeclaration);
N(SyntaxKind.AwaitExpression);
{
N(SyntaxKind.AwaitKeyword);
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "await");
}
N(SyntaxKind.VariableDeclarator);
{
N(SyntaxKind.IdentifierToken, "var");
}
}
N(SyntaxKind.SemicolonToken);
M(SyntaxKind.SemicolonToken);
}
EOF();
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册