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

Commit e687b62

Browse files
srawlinscommit-bot@chromium.org
authored andcommitted
Improve recovery on yield not-in generator; Improves #32967
Fix is inspired by very similar recover for `await` in synchronous function, like `looksLikeAwaitExpression` and `endInvalidAwaitExpression`. Change-Id: I50bf0c1e73c31551b94de29d78fd3d449c30fd20 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/136920 Commit-Queue: Samuel Rawlins <srawlins@google.com> Reviewed-by: Jens Johansen <jensj@google.com>
1 parent fbe9f61 commit e687b62

19 files changed

+548
-1
lines changed

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -800,6 +800,13 @@ class ForwardingListener implements Listener {
800800
listener?.endInvalidAwaitExpression(beginToken, endToken, errorCode);
801801
}
802802

803+
@override
804+
void endInvalidYieldStatement(Token beginToken, Token starToken,
805+
Token endToken, MessageCode errorCode) {
806+
listener?.endInvalidYieldStatement(
807+
beginToken, starToken, endToken, errorCode);
808+
}
809+
803810
@override
804811
void endLabeledStatement(int labelCount) {
805812
listener?.endLabeledStatement(labelCount);

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,11 @@ class Listener implements UnescapeErrorListener {
6868
logEvent("InvalidAwaitExpression");
6969
}
7070

71+
void endInvalidYieldStatement(Token beginToken, Token starToken,
72+
Token endToken, MessageCode errorCode) {
73+
logEvent("InvalidYieldStatement");
74+
}
75+
7176
void beginBlock(Token token, BlockKind blockKind) {}
7277

7378
void endBlock(

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

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4043,6 +4043,12 @@ class Parser {
40434043
if (optional(':', token.next.next)) {
40444044
return parseLabeledStatement(token);
40454045
}
4046+
if (looksLikeYieldStatement(token)) {
4047+
// Recovery: looks like an expression preceded by `yield` but not
4048+
// inside an Async or AsyncStar context. parseYieldStatement will
4049+
// report the error.
4050+
return parseYieldStatement(token);
4051+
}
40464052
return parseExpressionStatementOrDeclaration(token);
40474053

40484054
case AsyncModifier.SyncStar:
@@ -4097,7 +4103,13 @@ class Parser {
40974103
}
40984104
token = parseExpression(token);
40994105
token = ensureSemicolon(token);
4100-
listener.endYieldStatement(begin, starToken, token);
4106+
if (inPlainSync) {
4107+
codes.MessageCode errorCode = codes.messageYieldNotGenerator;
4108+
reportRecoverableError(begin, errorCode);
4109+
listener.endInvalidYieldStatement(begin, starToken, token, errorCode);
4110+
} else {
4111+
listener.endYieldStatement(begin, starToken, token);
4112+
}
41014113
return token;
41024114
}
41034115

@@ -6142,7 +6154,47 @@ class Parser {
61426154
} else if (isOneOf(token, ['.', ')', ']'])) {
61436155
return true;
61446156
}
6157+
// TODO(srawlins): Also consider when `token` is `;`. There is still not
6158+
// good error recovery on `await x;`.
6159+
}
6160+
// TODO(srawlins): Consider whether the token following `yield` is `null`
6161+
// (`token` would be `Keyword.NULL`) or is `<` as part of a type argument
6162+
// list. There is still not good error recovery on `await null` and on
6163+
// `await <int>[]`.
6164+
return false;
6165+
}
6166+
6167+
/// Determine if the following tokens look like a 'yield' expression and not a
6168+
/// local variable or local function declaration.
6169+
bool looksLikeYieldStatement(Token token) {
6170+
token = token.next;
6171+
assert(optional('yield', token));
6172+
token = token.next;
6173+
6174+
// TODO(srawlins): Consider parsing the potential expression following
6175+
// the `yield` token once doing so does not modify the token stream.
6176+
// For now, use simple look ahead and ensure no false positives.
6177+
6178+
if (token.isIdentifier) {
6179+
token = token.next;
6180+
if (optional('(', token)) {
6181+
token = token.endGroup.next;
6182+
if (isOneOf(token, [';', '.', '..', '?', '?.'])) {
6183+
return true;
6184+
}
6185+
} else if (isOneOf(token, ['.', ')', ']'])) {
6186+
// TODO(srawlins): Also consider when `token` is `;`. There is still not
6187+
// good error recovery on `yield x;`. This would also require
6188+
// modification to analyzer's
6189+
// test_parseCompilationUnit_pseudo_asTypeName.
6190+
return true;
6191+
}
6192+
} else if (token == Keyword.NULL) {
6193+
return true;
61456194
}
6195+
// TODO(srawlins): Consider whether the token following `yield` is `<` as
6196+
// part of a type argument list. There is still not good error recovery on
6197+
// `yield <int>[]`.
61466198
return false;
61476199
}
61486200

pkg/analyzer/lib/src/fasta/ast_builder.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1658,6 +1658,13 @@ class AstBuilder extends StackListener {
16581658
endAwaitExpression(awaitKeyword, endToken);
16591659
}
16601660

1661+
@override
1662+
void endInvalidYieldStatement(Token yieldKeyword, Token starToken,
1663+
Token endToken, MessageCode errorCode) {
1664+
debugEvent("InvalidYieldStatement");
1665+
endYieldStatement(yieldKeyword, starToken, endToken);
1666+
}
1667+
16611668
@override
16621669
void endLabeledStatement(int labelCount) {
16631670
debugEvent("LabeledStatement");

pkg/analyzer/test/generated/parser_fasta_listener.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -920,6 +920,13 @@ class ForwardingTestListener extends ForwardingListener {
920920
super.endInvalidAwaitExpression(beginToken, endToken, errorCode);
921921
}
922922

923+
@override
924+
void endInvalidYieldStatement(Token beginToken, Token starToken,
925+
Token endToken, MessageCode errorCode) {
926+
end('InvalidYieldStatement');
927+
super.endInvalidYieldStatement(beginToken, starToken, endToken, errorCode);
928+
}
929+
923930
@override
924931
void endLabeledStatement(int labelCount) {
925932
end('LabeledStatement');

pkg/front_end/lib/src/fasta/kernel/body_builder.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2770,6 +2770,14 @@ class BodyBuilder extends ScopeListener<JumpTarget>
27702770
push(buildProblem(errorCode, keyword.offset, keyword.length));
27712771
}
27722772

2773+
@override
2774+
void endInvalidYieldStatement(Token keyword, Token starToken, Token endToken,
2775+
fasta.MessageCode errorCode) {
2776+
debugEvent("YieldStatement");
2777+
popForValue();
2778+
push(buildProblemStatement(errorCode, keyword.offset));
2779+
}
2780+
27732781
@override
27742782
void handleAsyncModifier(Token asyncToken, Token starToken) {
27752783
debugEvent("AsyncModifier");
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Future<void> f() => Future.value();
2+
3+
void g() {
4+
await f();
5+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
Problems reported:
2+
3+
parser/error_recovery/await_not_in_async:4:3: 'await' can only be used in 'async' or 'async*' methods.
4+
await f();
5+
^^^^^
6+
7+
beginCompilationUnit(Future)
8+
beginMetadataStar(Future)
9+
endMetadataStar(0)
10+
beginTopLevelMember(Future)
11+
beginTopLevelMethod(, null)
12+
handleIdentifier(Future, typeReference)
13+
beginTypeArguments(<)
14+
handleVoidKeyword(void)
15+
endTypeArguments(1, <, >)
16+
handleType(Future, null)
17+
handleIdentifier(f, topLevelFunctionDeclaration)
18+
handleNoTypeVariables(()
19+
beginFormalParameters((, MemberKind.TopLevelMethod)
20+
endFormalParameters(0, (, ), MemberKind.TopLevelMethod)
21+
handleAsyncModifier(null, null)
22+
handleIdentifier(Future, expression)
23+
handleNoTypeArguments(.)
24+
handleNoArguments(.)
25+
handleSend(Future, .)
26+
handleIdentifier(value, expressionContinuation)
27+
handleNoTypeArguments(()
28+
beginArguments(()
29+
endArguments(0, (, ))
30+
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()
58+
endCompilationUnit(2, )
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
parseUnit(Future)
2+
skipErrorTokens(Future)
3+
listener: beginCompilationUnit(Future)
4+
syntheticPreviousToken(Future)
5+
parseTopLevelDeclarationImpl(, Instance of 'DirectiveContext')
6+
parseMetadataStar()
7+
listener: beginMetadataStar(Future)
8+
listener: endMetadataStar(0)
9+
parseTopLevelMemberImpl()
10+
listener: beginTopLevelMember(Future)
11+
parseTopLevelMethod(, null, , Instance of 'ComplexTypeInfo', null, f)
12+
listener: beginTopLevelMethod(, null)
13+
ensureIdentifier(, typeReference)
14+
listener: handleIdentifier(Future, typeReference)
15+
listener: beginTypeArguments(<)
16+
listener: handleVoidKeyword(void)
17+
listener: endTypeArguments(1, <, >)
18+
listener: handleType(Future, null)
19+
ensureIdentifier(>, topLevelFunctionDeclaration)
20+
listener: handleIdentifier(f, topLevelFunctionDeclaration)
21+
parseMethodTypeVar(f)
22+
listener: handleNoTypeVariables(()
23+
parseGetterOrFormalParameters(f, f, false, MemberKind.TopLevelMethod)
24+
parseFormalParameters(f, MemberKind.TopLevelMethod)
25+
parseFormalParametersRest((, MemberKind.TopLevelMethod)
26+
listener: beginFormalParameters((, MemberKind.TopLevelMethod)
27+
listener: endFormalParameters(0, (, ), MemberKind.TopLevelMethod)
28+
parseAsyncModifierOpt())
29+
listener: handleAsyncModifier(null, null)
30+
inPlainSync()
31+
parseFunctionBody(), false, false)
32+
parseExpressionFunctionBody(=>, false)
33+
parseExpression(=>)
34+
parsePrecedenceExpression(=>, 1, true)
35+
parseUnaryExpression(=>, true)
36+
parsePrimary(=>, expression)
37+
parseSendOrFunctionLiteral(=>, expression)
38+
parseSend(=>, expression)
39+
ensureIdentifier(=>, expression)
40+
listener: handleIdentifier(Future, expression)
41+
parseBangBeforeTypeArguments(Future)
42+
listener: handleNoTypeArguments(.)
43+
parseArgumentsOpt(Future)
44+
listener: handleNoArguments(.)
45+
listener: handleSend(Future, .)
46+
parsePrimary(., expressionContinuation)
47+
parseSendOrFunctionLiteral(., expressionContinuation)
48+
looksLikeFunctionBody(;)
49+
parseSend(., expressionContinuation)
50+
ensureIdentifier(., expressionContinuation)
51+
listener: handleIdentifier(value, expressionContinuation)
52+
parseBangBeforeTypeArguments(value)
53+
listener: handleNoTypeArguments(()
54+
parseArgumentsOpt(value)
55+
parseArguments(value)
56+
parseArgumentsRest(()
57+
listener: beginArguments(()
58+
listener: endArguments(0, (, ))
59+
listener: handleSend(value, ;)
60+
listener: endBinaryExpression(.)
61+
ensureSemicolon())
62+
listener: handleExpressionFunctionBody(=>, ;)
63+
inGenerator()
64+
listener: endTopLevelMethod(Future, null, ;)
65+
listener: endTopLevelDeclaration(void)
66+
parseTopLevelDeclarationImpl(;, Instance of 'DirectiveContext')
67+
parseMetadataStar(;)
68+
listener: beginMetadataStar(void)
69+
listener: endMetadataStar(0)
70+
parseTopLevelMemberImpl(;)
71+
listener: beginTopLevelMember(void)
72+
parseTopLevelMethod(;, null, ;, Instance of 'VoidType', null, g)
73+
listener: beginTopLevelMethod(;, null)
74+
listener: handleVoidKeyword(void)
75+
ensureIdentifier(void, topLevelFunctionDeclaration)
76+
listener: handleIdentifier(g, topLevelFunctionDeclaration)
77+
parseMethodTypeVar(g)
78+
listener: handleNoTypeVariables(()
79+
parseGetterOrFormalParameters(g, g, false, MemberKind.TopLevelMethod)
80+
parseFormalParameters(g, MemberKind.TopLevelMethod)
81+
parseFormalParametersRest((, MemberKind.TopLevelMethod)
82+
listener: beginFormalParameters((, MemberKind.TopLevelMethod)
83+
listener: endFormalParameters(0, (, ), MemberKind.TopLevelMethod)
84+
parseAsyncModifierOpt())
85+
listener: handleAsyncModifier(null, null)
86+
inPlainSync()
87+
parseFunctionBody(), false, false)
88+
listener: beginBlockFunctionBody({)
89+
notEofOrValue(}, await)
90+
parseStatement({)
91+
parseStatementX({)
92+
inPlainSync()
93+
looksLikeAwaitExpression({)
94+
parseExpressionStatement({)
95+
parseExpression({)
96+
parsePrecedenceExpression({, 1, true)
97+
parseUnaryExpression({, true)
98+
inPlainSync()
99+
looksLikeAwaitExpression({)
100+
parseAwaitExpression({, true)
101+
listener: beginAwaitExpression(await)
102+
parsePrecedenceExpression(await, 16, true)
103+
parseUnaryExpression(await, true)
104+
parsePrimary(await, expression)
105+
parseSendOrFunctionLiteral(await, expression)
106+
looksLikeFunctionBody(;)
107+
parseSend(await, expression)
108+
ensureIdentifier(await, expression)
109+
listener: handleIdentifier(f, expression)
110+
parseBangBeforeTypeArguments(f)
111+
listener: handleNoTypeArguments(()
112+
parseArgumentsOpt(f)
113+
parseArguments(f)
114+
parseArgumentsRest(()
115+
listener: beginArguments(()
116+
listener: endArguments(0, (, ))
117+
listener: handleSend(f, ;)
118+
inAsync()
119+
reportRecoverableError(await, AwaitNotAsync)
120+
listener: handleRecoverableError(AwaitNotAsync, await, await)
121+
listener: endInvalidAwaitExpression(await, ;, AwaitNotAsync)
122+
ensureSemicolon())
123+
listener: handleExpressionStatement(;)
124+
notEofOrValue(}, })
125+
listener: endBlockFunctionBody(1, {, })
126+
listener: endTopLevelMethod(void, null, })
127+
listener: endTopLevelDeclaration()
128+
reportAllErrorTokens(Future)
129+
listener: endCompilationUnit(2, )
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Future<void> f() => Future.value();
2+
3+
void g() {
4+
await f();
5+
}
6+
7+
8+
Future[StringToken]<[BeginToken]void[KeywordToken]>[SimpleToken] f[StringToken]([BeginToken])[SimpleToken] =>[SimpleToken] Future[StringToken].[SimpleToken]value[StringToken]([BeginToken])[SimpleToken];[SimpleToken]
9+
10+
void[KeywordToken] g[StringToken]([BeginToken])[SimpleToken] {[BeginToken]
11+
await[KeywordToken] f[StringToken]([BeginToken])[SimpleToken];[SimpleToken]
12+
}[SimpleToken]
13+
[SimpleToken]

0 commit comments

Comments
 (0)