Skip to content

Commit 4a7aebf

Browse files
author
Dart CI
committed
Version 2.18.0-236.0.dev
Merge commit 'dd9a2dca24a5a50bffcb088368b003414c737aa3' into 'dev'
2 parents ae74db5 + dd9a2dc commit 4a7aebf

17 files changed

+3752
-40
lines changed

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

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5015,7 +5015,7 @@ class Parser {
50155015
if (optional(':', token.next!.next!)) {
50165016
return parseLabeledStatement(token);
50175017
}
5018-
if (looksLikeYieldStatement(token)) {
5018+
if (looksLikeYieldStatement(token, AwaitOrYieldContext.Statement)) {
50195019
// Recovery: looks like an expression preceded by `yield` but not
50205020
// inside an Async or AsyncStar context. parseYieldStatement will
50215021
// report the error.
@@ -5035,7 +5035,7 @@ class Parser {
50355035
return parseExpressionStatementOrConstDeclaration(token);
50365036
} else if (identical(value, 'await')) {
50375037
if (inPlainSync) {
5038-
if (!looksLikeAwaitExpression(token)) {
5038+
if (!looksLikeAwaitExpression(token, AwaitOrYieldContext.Statement)) {
50395039
return parseExpressionStatementOrDeclaration(token);
50405040
}
50415041
// Recovery: looks like an expression preceded by `await`
@@ -5648,7 +5648,8 @@ class Parser {
56485648
// Prefix:
56495649
if (identical(value, 'await')) {
56505650
if (inPlainSync) {
5651-
if (!looksLikeAwaitExpression(token)) {
5651+
if (!looksLikeAwaitExpression(
5652+
token, AwaitOrYieldContext.UnaryExpression)) {
56525653
return parsePrimary(token, IdentifierContext.expression);
56535654
}
56545655
// Recovery: Looks like an expression preceded by `await`.
@@ -7562,7 +7563,8 @@ class Parser {
75627563

75637564
/// Determine if the following tokens look like an expression and not a local
75647565
/// variable or local function declaration.
7565-
bool looksLikeExpression(Token token) {
7566+
bool looksLikeExpressionAfterAwaitOrYield(
7567+
Token token, AwaitOrYieldContext context) {
75667568
// TODO(srawlins): Consider parsing the potential expression once doing so
75677569
// does not modify the token stream. For now, use simple look ahead and
75687570
// ensure no false positives.
@@ -7572,14 +7574,36 @@ class Parser {
75727574
token = token.next!;
75737575
if (optional('(', token)) {
75747576
token = token.endGroup!.next!;
7575-
if (isOneOf(token, [';', '.', '..', '?', '?.'])) {
7577+
if (isOneOf(token, const [';', '.', ',', '..', '?', '?.', ')'])) {
7578+
// E.g. (in a non-async function): `await f();`.
7579+
return true;
7580+
} else if (token.type.isBinaryOperator) {
7581+
// E.g. (in a non-async function):
7582+
// `await returnsFuture() + await returnsFuture()`.
75767583
return true;
75777584
}
7578-
} else if (isOneOf(token, ['.', ')', ']'])) {
7585+
} else if (isOneOf(token, const ['.', ')', ']'])) {
75797586
// TODO(srawlins): Also consider when `token` is `;`. There is still not
75807587
// good error recovery on `yield x;`. This would also require
75817588
// modification to analyzer's
75827589
// test_parseCompilationUnit_pseudo_asTypeName.
7590+
7591+
// E.g. (in a non-async function): `if (await f) {}`.
7592+
return true;
7593+
} else if (optional(',', token) &&
7594+
context == AwaitOrYieldContext.UnaryExpression) {
7595+
// E.g. (in a non-async function): `xor(await f, await f, await f);`,
7596+
// but not `await y, z` (`await` is a class here so it's declaring two
7597+
// variables).
7598+
return true;
7599+
} else if (token.type.isBinaryOperator) {
7600+
// E.g. (in a non-async function): (first part of) `await f + await f;`,
7601+
return true;
7602+
} else if (optional(';', token) &&
7603+
context == AwaitOrYieldContext.UnaryExpression) {
7604+
// E.g. (in a non-async function): (second part of) `await f + await f;`
7605+
// but not `await f;` (`await` is a class here so it's a variable
7606+
// declaration).
75837607
return true;
75847608
}
75857609
} else if (token == Keyword.NULL) {
@@ -7596,20 +7620,20 @@ class Parser {
75967620

75977621
/// Determine if the following tokens look like an 'await' expression
75987622
/// and not a local variable or local function declaration.
7599-
bool looksLikeAwaitExpression(Token token) {
7623+
bool looksLikeAwaitExpression(Token token, AwaitOrYieldContext context) {
76007624
token = token.next!;
76017625
assert(optional('await', token));
76027626

7603-
return looksLikeExpression(token);
7627+
return looksLikeExpressionAfterAwaitOrYield(token, context);
76047628
}
76057629

76067630
/// Determine if the following tokens look like a 'yield' expression and not a
76077631
/// local variable or local function declaration.
7608-
bool looksLikeYieldStatement(Token token) {
7632+
bool looksLikeYieldStatement(Token token, AwaitOrYieldContext context) {
76097633
token = token.next!;
76107634
assert(optional('yield', token));
76117635

7612-
return looksLikeExpression(token);
7636+
return looksLikeExpressionAfterAwaitOrYield(token, context);
76137637
}
76147638

76157639
/// ```
@@ -8724,3 +8748,5 @@ class Parser {
87248748

87258749
// TODO(ahe): Remove when analyzer supports generalized function syntax.
87268750
typedef _MessageWithArgument<T> = codes.Message Function(T);
8751+
8752+
enum AwaitOrYieldContext { Statement, UnaryExpression }

pkg/analyzer/test/generated/class_member_parser_test.dart

Lines changed: 131 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import 'package:analyzer/dart/ast/ast.dart';
66
import 'package:analyzer/dart/ast/token.dart';
77
import 'package:analyzer/src/dart/scanner/scanner.dart';
8+
import 'package:analyzer/src/error/codes.dart';
89
import 'package:test/test.dart';
910
import 'package:test_reflective_loader/test_reflective_loader.dart';
1011

@@ -143,15 +144,143 @@ class ClassMemberParserTest extends FastaParserTestCase
143144
var method = parser.parseClassMember('C') as MethodDeclaration;
144145
expect(method, isNotNull);
145146
listener.assertErrors([
146-
expectedError(ParserErrorCode.UNEXPECTED_TOKEN, 13, 5),
147-
expectedError(ParserErrorCode.UNEXPECTED_TOKEN, 23, 5)
147+
expectedError(CompileTimeErrorCode.AWAIT_IN_WRONG_CONTEXT, 13, 5),
148+
expectedError(CompileTimeErrorCode.AWAIT_IN_WRONG_CONTEXT, 23, 5)
148149
]);
149150
FunctionBody body = method.body;
150151
expect(body, isBlockFunctionBody);
151152
Statement statement = (body as BlockFunctionBody).block.statements[0];
152153
expect(statement, isReturnStatement);
153154
Expression expression = (statement as ReturnStatement).expression!;
154155
expect(expression, isBinaryExpression);
156+
expect((expression as BinaryExpression).leftOperand, isAwaitExpression);
157+
expect(expression.rightOperand, isAwaitExpression);
158+
}
159+
160+
void test_parseAwaitExpression_inSync_v1_49116() {
161+
createParser('m() { await returnsFuture(); }');
162+
var method = parser.parseClassMember('C') as MethodDeclaration;
163+
expect(method, isNotNull);
164+
listener.assertErrors([
165+
expectedError(CompileTimeErrorCode.AWAIT_IN_WRONG_CONTEXT, 6, 5),
166+
]);
167+
FunctionBody body = method.body;
168+
expect(body, isBlockFunctionBody);
169+
Statement statement = (body as BlockFunctionBody).block.statements[0];
170+
expect(statement, isExpressionStatement);
171+
Expression expression = (statement as ExpressionStatement).expression;
172+
expect(expression, isAwaitExpression);
173+
}
174+
175+
void test_parseAwaitExpression_inSync_v2_49116() {
176+
createParser('''m() {
177+
if (await returnsFuture()) {}
178+
else if (!await returnsFuture()) {}
179+
}''');
180+
var method = parser.parseClassMember('C') as MethodDeclaration;
181+
expect(method, isNotNull);
182+
listener.assertErrors([
183+
expectedError(CompileTimeErrorCode.AWAIT_IN_WRONG_CONTEXT, 16, 5),
184+
expectedError(CompileTimeErrorCode.AWAIT_IN_WRONG_CONTEXT, 58, 5),
185+
]);
186+
FunctionBody body = method.body;
187+
expect(body, isBlockFunctionBody);
188+
Statement statement = (body as BlockFunctionBody).block.statements[0];
189+
expect(statement, isIfStatement);
190+
Expression expression = (statement as IfStatement).condition;
191+
expect(expression, isAwaitExpression);
192+
expect(statement.elseStatement, isNotNull);
193+
Statement elseStatement = statement.elseStatement!;
194+
expect(elseStatement, isIfStatement);
195+
expression = (elseStatement as IfStatement).condition;
196+
expect(expression, isPrefixExpression);
197+
expect((expression as PrefixExpression).operator.lexeme, '!');
198+
expression = expression.operand;
199+
expect(expression, isAwaitExpression);
200+
}
201+
202+
void test_parseAwaitExpression_inSync_v3_49116() {
203+
createParser('m() { print(await returnsFuture()); }');
204+
var method = parser.parseClassMember('C') as MethodDeclaration;
205+
expect(method, isNotNull);
206+
listener.assertErrors([
207+
expectedError(CompileTimeErrorCode.AWAIT_IN_WRONG_CONTEXT, 12, 5),
208+
]);
209+
FunctionBody body = method.body;
210+
expect(body, isBlockFunctionBody);
211+
Statement statement = (body as BlockFunctionBody).block.statements[0];
212+
expect(statement, isExpressionStatement);
213+
Expression expression = (statement as ExpressionStatement).expression;
214+
expect(expression, isMethodInvocation);
215+
expression = (expression as MethodInvocation).argumentList.arguments.single;
216+
expect(expression, isAwaitExpression);
217+
}
218+
219+
void test_parseAwaitExpression_inSync_v4_49116() {
220+
createParser('''m() {
221+
xor(await returnsFuture(), await returnsFuture(), await returnsFuture());
222+
}''');
223+
var method = parser.parseClassMember('C') as MethodDeclaration;
224+
expect(method, isNotNull);
225+
listener.assertErrors([
226+
expectedError(CompileTimeErrorCode.AWAIT_IN_WRONG_CONTEXT, 16, 5),
227+
expectedError(CompileTimeErrorCode.AWAIT_IN_WRONG_CONTEXT, 39, 5),
228+
expectedError(CompileTimeErrorCode.AWAIT_IN_WRONG_CONTEXT, 62, 5),
229+
]);
230+
FunctionBody body = method.body;
231+
expect(body, isBlockFunctionBody);
232+
Statement statement = (body as BlockFunctionBody).block.statements[0];
233+
expect(statement, isExpressionStatement);
234+
Expression expression = (statement as ExpressionStatement).expression;
235+
expect(expression, isMethodInvocation);
236+
expect((expression as MethodInvocation).argumentList.arguments.length, 3);
237+
expect(expression.argumentList.arguments[0], isAwaitExpression);
238+
expect(expression.argumentList.arguments[1], isAwaitExpression);
239+
expect(expression.argumentList.arguments[2], isAwaitExpression);
240+
}
241+
242+
void test_parseAwaitExpression_inSync_v5_49116() {
243+
createParser('''m() {
244+
await returnsFuture() ^ await returnsFuture();
245+
}''');
246+
var method = parser.parseClassMember('C') as MethodDeclaration;
247+
expect(method, isNotNull);
248+
listener.assertErrors([
249+
expectedError(CompileTimeErrorCode.AWAIT_IN_WRONG_CONTEXT, 12, 5),
250+
expectedError(CompileTimeErrorCode.AWAIT_IN_WRONG_CONTEXT, 36, 5),
251+
]);
252+
FunctionBody body = method.body;
253+
expect(body, isBlockFunctionBody);
254+
Statement statement = (body as BlockFunctionBody).block.statements[0];
255+
expect(statement, isExpressionStatement);
256+
Expression expression = (statement as ExpressionStatement).expression;
257+
expect(expression, isBinaryExpression);
258+
expect((expression as BinaryExpression).leftOperand, isAwaitExpression);
259+
expect(expression.rightOperand, isAwaitExpression);
260+
expect(expression.operator.lexeme, '^');
261+
}
262+
263+
void test_parseAwaitExpression_inSync_v6_49116() {
264+
createParser('''m() {
265+
print(await returnsFuture() ^ await returnsFuture());
266+
}''');
267+
var method = parser.parseClassMember('C') as MethodDeclaration;
268+
expect(method, isNotNull);
269+
listener.assertErrors([
270+
expectedError(CompileTimeErrorCode.AWAIT_IN_WRONG_CONTEXT, 18, 5),
271+
expectedError(CompileTimeErrorCode.AWAIT_IN_WRONG_CONTEXT, 42, 5),
272+
]);
273+
FunctionBody body = method.body;
274+
expect(body, isBlockFunctionBody);
275+
Statement statement = (body as BlockFunctionBody).block.statements[0];
276+
expect(statement, isExpressionStatement);
277+
Expression expression = (statement as ExpressionStatement).expression;
278+
expect(expression, isMethodInvocation);
279+
expression = (expression as MethodInvocation).argumentList.arguments.single;
280+
expect(expression, isBinaryExpression);
281+
expect((expression as BinaryExpression).leftOperand, isAwaitExpression);
282+
expect(expression.rightOperand, isAwaitExpression);
283+
expect(expression.operator.lexeme, '^');
155284
}
156285

157286
void test_parseClassMember_constructor_initializers_conditional() {

pkg/analyzer/test/generated/parser_fasta_listener.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1002,7 +1002,8 @@ class ForwardingTestListener extends ForwardingListener {
10021002
@override
10031003
void endInvalidAwaitExpression(
10041004
Token beginToken, Token endToken, MessageCode errorCode) {
1005-
end('InvalidAwaitExpression');
1005+
// endInvalidAwaitExpression is started by beginAwaitExpression
1006+
end('AwaitExpression');
10061007
super.endInvalidAwaitExpression(beginToken, endToken, errorCode);
10071008
}
10081009

pkg/analyzer/test/src/diagnostics/await_in_wrong_context_test.dart

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,7 @@ main() {
1515

1616
@reflectiveTest
1717
class AwaitInWrongContextTest extends PubPackageResolutionTest {
18-
@failingTest
1918
test_sync() async {
20-
// This test requires better error recovery than we currently have. In
21-
// particular, we need to be able to distinguish between an await expression
22-
// in the wrong context, and the use of 'await' as an identifier.
2319
await assertErrorsInCode(r'''
2420
f(x) {
2521
return await x;
@@ -30,9 +26,6 @@ f(x) {
3026
}
3127

3228
test_syncStar() async {
33-
// This test requires better error recovery than we currently have. In
34-
// particular, we need to be able to distinguish between an await expression
35-
// in the wrong context, and the use of 'await' as an identifier.
3629
await assertErrorsInCode(r'''
3730
f(x) sync* {
3831
yield await x;

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,15 +90,15 @@ parseUnit(Future)
9090
parseStatement({)
9191
parseStatementX({)
9292
inPlainSync()
93-
looksLikeAwaitExpression({)
94-
looksLikeExpression(await)
93+
looksLikeAwaitExpression({, AwaitOrYieldContext.Statement)
94+
looksLikeExpressionAfterAwaitOrYield(await, AwaitOrYieldContext.Statement)
9595
parseExpressionStatement({)
9696
parseExpression({)
9797
parsePrecedenceExpression({, 1, true)
9898
parseUnaryExpression({, true)
9999
inPlainSync()
100-
looksLikeAwaitExpression({)
101-
looksLikeExpression(await)
100+
looksLikeAwaitExpression({, AwaitOrYieldContext.UnaryExpression)
101+
looksLikeExpressionAfterAwaitOrYield(await, AwaitOrYieldContext.UnaryExpression)
102102
parseAwaitExpression({, true)
103103
listener: beginAwaitExpression(await)
104104
parsePrecedenceExpression(await, 16, true)
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
Future<bool> returnsFuture() => new Future.value(true);
2+
3+
// Notice the missing async marker.
4+
void foo() {
5+
await returnsFuture();
6+
if (await returnsFuture()) {}
7+
else if (!await returnsFuture()) {}
8+
print(await returnsFuture());
9+
xor(await returnsFuture(), await returnsFuture(), await returnsFuture());
10+
await returnsFuture() ^ await returnsFuture();
11+
print(await returnsFuture() ^ await returnsFuture());
12+
await returnsFuture() + await returnsFuture();
13+
print(await returnsFuture() + await returnsFuture());
14+
await returnsFuture() - await returnsFuture();
15+
print(await returnsFuture() - await returnsFuture());
16+
!await returnsFuture() ^ !await returnsFuture();
17+
print(!await returnsFuture() ^ !await returnsFuture());
18+
19+
var f = returnsFuture();
20+
await f; // valid variable declaration.
21+
if (await f) {}
22+
else if (!await f) {}
23+
print(await f);
24+
xor(await f, await f, await f);
25+
await f ^ await f;
26+
print(await f ^ await f);
27+
await f + await f;
28+
print(await f + await f);
29+
await f - await f;
30+
print(await f - await f);
31+
!await f ^ !await f;
32+
print(!await f ^ !await f);
33+
34+
// Valid:
35+
await x; // Valid.
36+
await y, z; // Valid.
37+
await x2 = await; // Valid.
38+
await y2 = await, z2 = await; // Valid.
39+
await foo(int bar) { // Valid.
40+
return new await(); // Valid.
41+
} // Valid.
42+
await bar(await baz, await baz2, await baz3) { // Valid.
43+
return baz; // Valid.
44+
} // Valid.
45+
}
46+
47+
bool xor(bool a, bool b, bool c) {
48+
return b ^ b ^ c;
49+
}
50+
51+
class await {}

0 commit comments

Comments
 (0)