Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit e7e4559

Browse files
srawlinscommit-bot@chromium.org
authored andcommitted
Improve error recovery of misplaced await or yield
Bug: #32967 Change-Id: I9f8e2ae434ea79470949e7931bcb80c542c54f33 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/138042 Reviewed-by: Jens Johansen <jensj@google.com> Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Commit-Queue: Samuel Rawlins <srawlins@google.com>
1 parent c1ff1cb commit e7e4559

File tree

6 files changed

+96
-108
lines changed

6 files changed

+96
-108
lines changed

pkg/_fe_analyzer_shared/lib/src/parser/parser_impl.dart

Lines changed: 35 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -4104,8 +4104,14 @@ class Parser {
41044104
token = parseExpression(token);
41054105
token = ensureSemicolon(token);
41064106
if (inPlainSync) {
4107+
// `yield` is only allowed in generators; A recoverable error is already
4108+
// reported in the "async" case in `parseStatementX`. Only the "sync" case
4109+
// needs to be handled here.
41074110
codes.MessageCode errorCode = codes.messageYieldNotGenerator;
41084111
reportRecoverableError(begin, errorCode);
4112+
// TODO(srawlins): Add tests in analyzer to ensure the AstBuilder
4113+
// correctly handles invalid yields, and that the error message is
4114+
// correctly plumbed through.
41094115
listener.endInvalidYieldStatement(begin, starToken, token, errorCode);
41104116
} else {
41114117
listener.endYieldStatement(begin, starToken, token);
@@ -6134,48 +6140,14 @@ class Parser {
61346140
return token;
61356141
}
61366142

6137-
/// Determine if the following tokens look like an 'await' expression
6138-
/// and not a local variable or local function declaration.
6139-
bool looksLikeAwaitExpression(Token token) {
6140-
token = token.next;
6141-
assert(optional('await', token));
6142-
token = token.next;
6143+
/// Determine if the following tokens look like an expression and not a local
6144+
/// variable or local function declaration.
6145+
bool looksLikeExpression(Token token) {
6146+
// TODO(srawlins): Consider parsing the potential expression once doing so
6147+
// does not modify the token stream. For now, use simple look ahead and
6148+
// ensure no false positives.
61436149

6144-
// TODO(danrubel): Consider parsing the potential expression following
6145-
// the `await` token once doing so does not modify the token stream.
6146-
// For now, use simple look ahead and ensure no false positives.
6147-
6148-
if (token.isIdentifier) {
6149-
token = token.next;
6150-
if (optional('(', token)) {
6151-
token = token.endGroup.next;
6152-
if (isOneOf(token, [';', '.', '..', '?', '?.'])) {
6153-
return true;
6154-
}
6155-
} else if (isOneOf(token, ['.', ')', ']'])) {
6156-
return true;
6157-
}
6158-
// TODO(srawlins): Also consider when `token` is `;`. There is still not
6159-
// good error recovery on `await x;`.
6160-
}
6161-
// TODO(srawlins): Consider whether the token following `yield` is `null`
6162-
// (`token` would be `Keyword.NULL`) or is `<` as part of a type argument
6163-
// list. There is still not good error recovery on `await null` and on
6164-
// `await <int>[]`.
6165-
return false;
6166-
}
6167-
6168-
/// Determine if the following tokens look like a 'yield' expression and not a
6169-
/// local variable or local function declaration.
6170-
bool looksLikeYieldStatement(Token token) {
6171-
token = token.next;
6172-
assert(optional('yield', token));
61736150
token = token.next;
6174-
6175-
// TODO(srawlins): Consider parsing the potential expression following
6176-
// the `yield` token once doing so does not modify the token stream.
6177-
// For now, use simple look ahead and ensure no false positives.
6178-
61796151
if (token.isIdentifier) {
61806152
token = token.next;
61816153
if (optional('(', token)) {
@@ -6193,12 +6165,33 @@ class Parser {
61936165
} else if (token == Keyword.NULL) {
61946166
return true;
61956167
}
6196-
// TODO(srawlins): Consider whether the token following `yield` is `<` as
6197-
// part of a type argument list. There is still not good error recovery on
6168+
// TODO(srawlins): Consider other possibilities for `token` which would
6169+
// imply it looks like an expression, for example beginning with `<`, as
6170+
// part of a collection literal type argument list, `(`, other literals,
6171+
// etc. For example, there is still not good error recovery on
61986172
// `yield <int>[]`.
6173+
61996174
return false;
62006175
}
62016176

6177+
/// Determine if the following tokens look like an 'await' expression
6178+
/// and not a local variable or local function declaration.
6179+
bool looksLikeAwaitExpression(Token token) {
6180+
token = token.next;
6181+
assert(optional('await', token));
6182+
6183+
return looksLikeExpression(token);
6184+
}
6185+
6186+
/// Determine if the following tokens look like a 'yield' expression and not a
6187+
/// local variable or local function declaration.
6188+
bool looksLikeYieldStatement(Token token) {
6189+
token = token.next;
6190+
assert(optional('yield', token));
6191+
6192+
return looksLikeExpression(token);
6193+
}
6194+
62026195
/// ```
62036196
/// awaitExpression:
62046197
/// 'await' unaryExpression

pkg/front_end/parser_testcases/error_recovery/await_not_in_async.dart.expect

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -28,31 +28,31 @@ beginCompilationUnit(Future)
2828
beginArguments(()
2929
endArguments(0, (, ))
3030
handleSend(value, ;)
31-
endBinaryExpression(.)
32-
handleExpressionFunctionBody(=>, ;)
33-
endTopLevelMethod(Future, null, ;)
34-
endTopLevelDeclaration(void)
35-
beginMetadataStar(void)
36-
endMetadataStar(0)
37-
beginTopLevelMember(void)
38-
beginTopLevelMethod(;, null)
39-
handleVoidKeyword(void)
40-
handleIdentifier(g, topLevelFunctionDeclaration)
41-
handleNoTypeVariables(()
42-
beginFormalParameters((, MemberKind.TopLevelMethod)
43-
endFormalParameters(0, (, ), MemberKind.TopLevelMethod)
44-
handleAsyncModifier(null, null)
45-
beginBlockFunctionBody({)
46-
beginAwaitExpression(await)
47-
handleIdentifier(f, expression)
48-
handleNoTypeArguments(()
49-
beginArguments(()
50-
endArguments(0, (, ))
51-
handleSend(f, ;)
52-
handleRecoverableError(AwaitNotAsync, await, await)
53-
endInvalidAwaitExpression(await, ;, AwaitNotAsync)
54-
handleExpressionStatement(;)
55-
endBlockFunctionBody(1, {, })
56-
endTopLevelMethod(void, null, })
57-
endTopLevelDeclaration()
31+
endBinaryExpression(.)
32+
handleExpressionFunctionBody(=>, ;)
33+
endTopLevelMethod(Future, null, ;)
34+
endTopLevelDeclaration(void)
35+
beginMetadataStar(void)
36+
endMetadataStar(0)
37+
beginTopLevelMember(void)
38+
beginTopLevelMethod(;, null)
39+
handleVoidKeyword(void)
40+
handleIdentifier(g, topLevelFunctionDeclaration)
41+
handleNoTypeVariables(()
42+
beginFormalParameters((, MemberKind.TopLevelMethod)
43+
endFormalParameters(0, (, ), MemberKind.TopLevelMethod)
44+
handleAsyncModifier(null, null)
45+
beginBlockFunctionBody({)
46+
beginAwaitExpression(await)
47+
handleIdentifier(f, expression)
48+
handleNoTypeArguments(()
49+
beginArguments(()
50+
endArguments(0, (, ))
51+
handleSend(f, ;)
52+
handleRecoverableError(AwaitNotAsync, await, await)
53+
endInvalidAwaitExpression(await, ;, AwaitNotAsync)
54+
handleExpressionStatement(;)
55+
endBlockFunctionBody(1, {, })
56+
endTopLevelMethod(void, null, })
57+
endTopLevelDeclaration()
5858
endCompilationUnit(2, )

pkg/front_end/parser_testcases/error_recovery/await_not_in_async.dart.intertwined.expect

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ parseUnit(Future)
3838
parseSend(=>, expression)
3939
ensureIdentifier(=>, expression)
4040
listener: handleIdentifier(Future, expression)
41-
parseBangBeforeTypeArguments(Future)
4241
listener: handleNoTypeArguments(.)
4342
parseArgumentsOpt(Future)
4443
listener: handleNoArguments(.)
@@ -49,7 +48,6 @@ parseUnit(Future)
4948
parseSend(., expressionContinuation)
5049
ensureIdentifier(., expressionContinuation)
5150
listener: handleIdentifier(value, expressionContinuation)
52-
parseBangBeforeTypeArguments(value)
5351
listener: handleNoTypeArguments(()
5452
parseArgumentsOpt(value)
5553
parseArguments(value)
@@ -107,7 +105,6 @@ parseUnit(Future)
107105
parseSend(await, expression)
108106
ensureIdentifier(await, expression)
109107
listener: handleIdentifier(f, expression)
110-
parseBangBeforeTypeArguments(f)
111108
listener: handleNoTypeArguments(()
112109
parseArgumentsOpt(f)
113110
parseArguments(f)

pkg/front_end/parser_testcases/error_recovery/yield_not_in_generator.dart.expect

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -31,36 +31,36 @@ beginCompilationUnit(Future)
3131
handleLiteralInt(7)
3232
endArguments(1, (, ))
3333
handleSend(value, ;)
34-
endBinaryExpression(.)
35-
handleExpressionFunctionBody(=>, ;)
36-
endTopLevelMethod(Future, null, ;)
37-
endTopLevelDeclaration(List)
38-
beginMetadataStar(List)
39-
endMetadataStar(0)
40-
beginTopLevelMember(List)
41-
beginTopLevelMethod(;, null)
42-
handleIdentifier(List, typeReference)
43-
beginTypeArguments(<)
44-
handleIdentifier(int, typeReference)
45-
handleNoTypeArguments(>)
46-
handleType(int, null)
47-
endTypeArguments(1, <, >)
48-
handleType(List, null)
49-
handleIdentifier(g, topLevelFunctionDeclaration)
50-
handleNoTypeVariables(()
51-
beginFormalParameters((, MemberKind.TopLevelMethod)
52-
endFormalParameters(0, (, ), MemberKind.TopLevelMethod)
53-
handleAsyncModifier(null, null)
54-
beginBlockFunctionBody({)
55-
beginYieldStatement(yield)
56-
handleIdentifier(f, expression)
57-
handleNoTypeArguments(()
58-
beginArguments(()
59-
endArguments(0, (, ))
60-
handleSend(f, ;)
61-
handleRecoverableError(YieldNotGenerator, yield, yield)
62-
endInvalidYieldStatement(yield, null, ;, YieldNotGenerator)
63-
endBlockFunctionBody(1, {, })
64-
endTopLevelMethod(List, null, })
65-
endTopLevelDeclaration()
34+
endBinaryExpression(.)
35+
handleExpressionFunctionBody(=>, ;)
36+
endTopLevelMethod(Future, null, ;)
37+
endTopLevelDeclaration(List)
38+
beginMetadataStar(List)
39+
endMetadataStar(0)
40+
beginTopLevelMember(List)
41+
beginTopLevelMethod(;, null)
42+
handleIdentifier(List, typeReference)
43+
beginTypeArguments(<)
44+
handleIdentifier(int, typeReference)
45+
handleNoTypeArguments(>)
46+
handleType(int, null)
47+
endTypeArguments(1, <, >)
48+
handleType(List, null)
49+
handleIdentifier(g, topLevelFunctionDeclaration)
50+
handleNoTypeVariables(()
51+
beginFormalParameters((, MemberKind.TopLevelMethod)
52+
endFormalParameters(0, (, ), MemberKind.TopLevelMethod)
53+
handleAsyncModifier(null, null)
54+
beginBlockFunctionBody({)
55+
beginYieldStatement(yield)
56+
handleIdentifier(f, expression)
57+
handleNoTypeArguments(()
58+
beginArguments(()
59+
endArguments(0, (, ))
60+
handleSend(f, ;)
61+
handleRecoverableError(YieldNotGenerator, yield, yield)
62+
endInvalidYieldStatement(yield, null, ;, YieldNotGenerator)
63+
endBlockFunctionBody(1, {, })
64+
endTopLevelMethod(List, null, })
65+
endTopLevelDeclaration()
6666
endCompilationUnit(2, )

pkg/front_end/parser_testcases/error_recovery/yield_not_in_generator.dart.intertwined.expect

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ parseUnit(Future)
3939
parseSend(=>, expression)
4040
ensureIdentifier(=>, expression)
4141
listener: handleIdentifier(Future, expression)
42-
parseBangBeforeTypeArguments(Future)
4342
listener: handleNoTypeArguments(.)
4443
parseArgumentsOpt(Future)
4544
listener: handleNoArguments(.)
@@ -50,7 +49,6 @@ parseUnit(Future)
5049
parseSend(., expressionContinuation)
5150
ensureIdentifier(., expressionContinuation)
5251
listener: handleIdentifier(value, expressionContinuation)
53-
parseBangBeforeTypeArguments(value)
5452
listener: handleNoTypeArguments(()
5553
parseArgumentsOpt(value)
5654
parseArguments(value)
@@ -113,7 +111,6 @@ parseUnit(Future)
113111
parseSend(yield, expression)
114112
ensureIdentifier(yield, expression)
115113
listener: handleIdentifier(f, expression)
116-
parseBangBeforeTypeArguments(f)
117114
listener: handleNoTypeArguments(()
118115
parseArgumentsOpt(f)
119116
parseArguments(f)
@@ -122,7 +119,7 @@ parseUnit(Future)
122119
listener: endArguments(0, (, ))
123120
listener: handleSend(f, ;)
124121
ensureSemicolon())
125-
inPlainSync()
122+
inGenerator()
126123
reportRecoverableError(yield, YieldNotGenerator)
127124
listener: handleRecoverableError(YieldNotGenerator, yield, yield)
128125
listener: endInvalidYieldStatement(yield, null, ;, YieldNotGenerator)

pkg/front_end/test/spell_checking_list_common.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2111,6 +2111,7 @@ platform's
21112111
platforms
21122112
please
21132113
plethora
2114+
plumbed
21142115
plus
21152116
point
21162117
pointer

0 commit comments

Comments
 (0)