[parser] Use endInvalidYieldStatement for async methods

This changes the parser to call endInvalidYieldStatement for
yield statement both in sync and async methods. This ensures that
the CFE creates an invalid expression for yield in async methods,
making the generated AST verifiable.

Change-Id: I1bfe922878fcf3dc19c823cb89ee869af3dd2ce3
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/309200
Commit-Queue: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Jens Johansen <jensj@google.com>
This commit is contained in:
Johnni Winther 2023-06-14 12:10:59 +00:00 committed by Commit Queue
parent 1100859b7e
commit e5a68252e3
11 changed files with 381 additions and 9 deletions

View file

@ -5484,7 +5484,6 @@ class Parser {
return parseYieldStatement(token);
case AsyncModifier.Async:
reportRecoverableError(token.next!, codes.messageYieldNotGenerator);
return parseYieldStatement(token);
}
} else if (identical(value, 'const')) {
@ -5530,18 +5529,15 @@ class Parser {
}
token = parseExpression(token);
token = ensureSemicolon(token);
if (inPlainSync) {
// `yield` is only allowed in generators; A recoverable error is already
// reported in the "async" case in `parseStatementX`. Only the "sync" case
// needs to be handled here.
if (inGenerator) {
listener.endYieldStatement(begin, starToken, token);
} else {
codes.MessageCode errorCode = codes.messageYieldNotGenerator;
reportRecoverableError(begin, errorCode);
// TODO(srawlins): Add tests in analyzer to ensure the AstBuilder
// correctly handles invalid yields, and that the error message is
// correctly plumbed through.
listener.endInvalidYieldStatement(begin, starToken, token, errorCode);
} else {
listener.endYieldStatement(begin, starToken, token);
}
return token;
}
@ -8484,7 +8480,7 @@ class Parser {
// declaration).
return true;
}
} else if (token == Keyword.NULL) {
} else if (token.keyword == Keyword.NULL) {
return true;
}
// TODO(srawlins): Consider other possibilities for `token` which would

View file

@ -124,7 +124,7 @@ parseUnit(Future)
listener: endArguments(0, (, ))
listener: handleSend(f, ;)
ensureSemicolon())
inPlainSync()
inGenerator()
reportRecoverableError(yield, YieldNotGenerator)
listener: handleRecoverableError(YieldNotGenerator, yield, yield)
listener: endInvalidYieldStatement(yield, null, ;, YieldNotGenerator)

View file

@ -0,0 +1,22 @@
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
method1(var a) {
yield null;
yield a;
yield a();
yield 1;
}
method2() {
yield* [1];
}
method3() async {
yield 1;
}
method4() async {
yield [1];
}

View file

@ -0,0 +1,67 @@
library;
//
// Problems in library:
//
// pkg/front_end/testcases/general/invalid_yield.dart:6:3: Error: 'yield' can only be used in 'sync*' or 'async*' methods.
// yield null;
// ^^^^^
//
// pkg/front_end/testcases/general/invalid_yield.dart:7:3: Error: 'yield' isn't a type.
// yield a;
// ^^^^^
//
// pkg/front_end/testcases/general/invalid_yield.dart:8:3: Error: 'yield' can only be used in 'sync*' or 'async*' methods.
// yield a();
// ^^^^^
//
// pkg/front_end/testcases/general/invalid_yield.dart:9:3: Error: Expected ';' after this.
// yield 1;
// ^^^^^
//
// pkg/front_end/testcases/general/invalid_yield.dart:9:3: Error: Undefined name 'yield'.
// yield 1;
// ^^^^^
//
// pkg/front_end/testcases/general/invalid_yield.dart:13:3: Error: Undefined name 'yield'.
// yield* [1];
// ^^^^^
//
// pkg/front_end/testcases/general/invalid_yield.dart:17:3: Error: 'yield' can only be used in 'sync*' or 'async*' methods.
// yield 1;
// ^^^^^
//
// pkg/front_end/testcases/general/invalid_yield.dart:21:3: Error: 'yield' can only be used in 'sync*' or 'async*' methods.
// yield [1];
// ^^^^^
//
import self as self;
import "dart:core" as core;
static method method1(dynamic a) → dynamic {
invalid-expression "pkg/front_end/testcases/general/invalid_yield.dart:6:3: Error: 'yield' can only be used in 'sync*' or 'async*' methods.
yield null;
^";
invalid-type a;
invalid-expression "pkg/front_end/testcases/general/invalid_yield.dart:8:3: Error: 'yield' can only be used in 'sync*' or 'async*' methods.
yield a();
^";
invalid-expression "pkg/front_end/testcases/general/invalid_yield.dart:9:3: Error: Undefined name 'yield'.
yield 1;
^^^^^";
1;
}
static method method2() → dynamic {
invalid-expression "pkg/front_end/testcases/general/invalid_yield.dart:13:3: Error: Undefined name 'yield'.
yield* [1];
^^^^^"{<invalid>}.*(<core::int>[1]);
}
static method method3() → dynamic async /* futureValueType= dynamic */ {
invalid-expression "pkg/front_end/testcases/general/invalid_yield.dart:17:3: Error: 'yield' can only be used in 'sync*' or 'async*' methods.
yield 1;
^";
}
static method method4() → dynamic async /* futureValueType= dynamic */ {
invalid-expression "pkg/front_end/testcases/general/invalid_yield.dart:21:3: Error: 'yield' can only be used in 'sync*' or 'async*' methods.
yield [1];
^";
}

View file

@ -0,0 +1,67 @@
library;
//
// Problems in library:
//
// pkg/front_end/testcases/general/invalid_yield.dart:6:3: Error: 'yield' can only be used in 'sync*' or 'async*' methods.
// yield null;
// ^^^^^
//
// pkg/front_end/testcases/general/invalid_yield.dart:7:3: Error: 'yield' isn't a type.
// yield a;
// ^^^^^
//
// pkg/front_end/testcases/general/invalid_yield.dart:8:3: Error: 'yield' can only be used in 'sync*' or 'async*' methods.
// yield a();
// ^^^^^
//
// pkg/front_end/testcases/general/invalid_yield.dart:9:3: Error: Expected ';' after this.
// yield 1;
// ^^^^^
//
// pkg/front_end/testcases/general/invalid_yield.dart:9:3: Error: Undefined name 'yield'.
// yield 1;
// ^^^^^
//
// pkg/front_end/testcases/general/invalid_yield.dart:13:3: Error: Undefined name 'yield'.
// yield* [1];
// ^^^^^
//
// pkg/front_end/testcases/general/invalid_yield.dart:17:3: Error: 'yield' can only be used in 'sync*' or 'async*' methods.
// yield 1;
// ^^^^^
//
// pkg/front_end/testcases/general/invalid_yield.dart:21:3: Error: 'yield' can only be used in 'sync*' or 'async*' methods.
// yield [1];
// ^^^^^
//
import self as self;
import "dart:core" as core;
static method method1(dynamic a) → dynamic {
invalid-expression "pkg/front_end/testcases/general/invalid_yield.dart:6:3: Error: 'yield' can only be used in 'sync*' or 'async*' methods.
yield null;
^";
invalid-type a;
invalid-expression "pkg/front_end/testcases/general/invalid_yield.dart:8:3: Error: 'yield' can only be used in 'sync*' or 'async*' methods.
yield a();
^";
invalid-expression "pkg/front_end/testcases/general/invalid_yield.dart:9:3: Error: Undefined name 'yield'.
yield 1;
^^^^^";
1;
}
static method method2() → dynamic {
invalid-expression "pkg/front_end/testcases/general/invalid_yield.dart:13:3: Error: Undefined name 'yield'.
yield* [1];
^^^^^"{<invalid>}.*(core::_GrowableList::_literal1<core::int>(1));
}
static method method3() → dynamic async /* futureValueType= dynamic */ {
invalid-expression "pkg/front_end/testcases/general/invalid_yield.dart:17:3: Error: 'yield' can only be used in 'sync*' or 'async*' methods.
yield 1;
^";
}
static method method4() → dynamic async /* futureValueType= dynamic */ {
invalid-expression "pkg/front_end/testcases/general/invalid_yield.dart:21:3: Error: 'yield' can only be used in 'sync*' or 'async*' methods.
yield [1];
^";
}

View file

@ -0,0 +1,4 @@
method1(var a) {}
method2() {}
method3() async {}
method4() async {}

View file

@ -0,0 +1,4 @@
method1(var a) {}
method2() {}
method3() async {}
method4() async {}

View file

@ -0,0 +1,67 @@
library;
//
// Problems in library:
//
// pkg/front_end/testcases/general/invalid_yield.dart:6:3: Error: 'yield' can only be used in 'sync*' or 'async*' methods.
// yield null;
// ^^^^^
//
// pkg/front_end/testcases/general/invalid_yield.dart:7:3: Error: 'yield' isn't a type.
// yield a;
// ^^^^^
//
// pkg/front_end/testcases/general/invalid_yield.dart:8:3: Error: 'yield' can only be used in 'sync*' or 'async*' methods.
// yield a();
// ^^^^^
//
// pkg/front_end/testcases/general/invalid_yield.dart:9:3: Error: Expected ';' after this.
// yield 1;
// ^^^^^
//
// pkg/front_end/testcases/general/invalid_yield.dart:9:3: Error: Undefined name 'yield'.
// yield 1;
// ^^^^^
//
// pkg/front_end/testcases/general/invalid_yield.dart:13:3: Error: Undefined name 'yield'.
// yield* [1];
// ^^^^^
//
// pkg/front_end/testcases/general/invalid_yield.dart:17:3: Error: 'yield' can only be used in 'sync*' or 'async*' methods.
// yield 1;
// ^^^^^
//
// pkg/front_end/testcases/general/invalid_yield.dart:21:3: Error: 'yield' can only be used in 'sync*' or 'async*' methods.
// yield [1];
// ^^^^^
//
import self as self;
import "dart:core" as core;
static method method1(dynamic a) → dynamic {
invalid-expression "pkg/front_end/testcases/general/invalid_yield.dart:6:3: Error: 'yield' can only be used in 'sync*' or 'async*' methods.
yield null;
^";
invalid-type a;
invalid-expression "pkg/front_end/testcases/general/invalid_yield.dart:8:3: Error: 'yield' can only be used in 'sync*' or 'async*' methods.
yield a();
^";
invalid-expression "pkg/front_end/testcases/general/invalid_yield.dart:9:3: Error: Undefined name 'yield'.
yield 1;
^^^^^";
1;
}
static method method2() → dynamic {
invalid-expression "pkg/front_end/testcases/general/invalid_yield.dart:13:3: Error: Undefined name 'yield'.
yield* [1];
^^^^^"{<invalid>}.*(<core::int>[1]);
}
static method method3() → dynamic async /* futureValueType= dynamic */ {
invalid-expression "pkg/front_end/testcases/general/invalid_yield.dart:17:3: Error: 'yield' can only be used in 'sync*' or 'async*' methods.
yield 1;
^";
}
static method method4() → dynamic async /* futureValueType= dynamic */ {
invalid-expression "pkg/front_end/testcases/general/invalid_yield.dart:21:3: Error: 'yield' can only be used in 'sync*' or 'async*' methods.
yield [1];
^";
}

View file

@ -0,0 +1,67 @@
library;
//
// Problems in library:
//
// pkg/front_end/testcases/general/invalid_yield.dart:6:3: Error: 'yield' can only be used in 'sync*' or 'async*' methods.
// yield null;
// ^^^^^
//
// pkg/front_end/testcases/general/invalid_yield.dart:7:3: Error: 'yield' isn't a type.
// yield a;
// ^^^^^
//
// pkg/front_end/testcases/general/invalid_yield.dart:8:3: Error: 'yield' can only be used in 'sync*' or 'async*' methods.
// yield a();
// ^^^^^
//
// pkg/front_end/testcases/general/invalid_yield.dart:9:3: Error: Expected ';' after this.
// yield 1;
// ^^^^^
//
// pkg/front_end/testcases/general/invalid_yield.dart:9:3: Error: Undefined name 'yield'.
// yield 1;
// ^^^^^
//
// pkg/front_end/testcases/general/invalid_yield.dart:13:3: Error: Undefined name 'yield'.
// yield* [1];
// ^^^^^
//
// pkg/front_end/testcases/general/invalid_yield.dart:17:3: Error: 'yield' can only be used in 'sync*' or 'async*' methods.
// yield 1;
// ^^^^^
//
// pkg/front_end/testcases/general/invalid_yield.dart:21:3: Error: 'yield' can only be used in 'sync*' or 'async*' methods.
// yield [1];
// ^^^^^
//
import self as self;
import "dart:core" as core;
static method method1(dynamic a) → dynamic {
invalid-expression "pkg/front_end/testcases/general/invalid_yield.dart:6:3: Error: 'yield' can only be used in 'sync*' or 'async*' methods.
yield null;
^";
invalid-type a;
invalid-expression "pkg/front_end/testcases/general/invalid_yield.dart:8:3: Error: 'yield' can only be used in 'sync*' or 'async*' methods.
yield a();
^";
invalid-expression "pkg/front_end/testcases/general/invalid_yield.dart:9:3: Error: Undefined name 'yield'.
yield 1;
^^^^^";
1;
}
static method method2() → dynamic {
invalid-expression "pkg/front_end/testcases/general/invalid_yield.dart:13:3: Error: Undefined name 'yield'.
yield* [1];
^^^^^"{<invalid>}.*(<core::int>[1]);
}
static method method3() → dynamic async /* futureValueType= dynamic */ {
invalid-expression "pkg/front_end/testcases/general/invalid_yield.dart:17:3: Error: 'yield' can only be used in 'sync*' or 'async*' methods.
yield 1;
^";
}
static method method4() → dynamic async /* futureValueType= dynamic */ {
invalid-expression "pkg/front_end/testcases/general/invalid_yield.dart:21:3: Error: 'yield' can only be used in 'sync*' or 'async*' methods.
yield [1];
^";
}

View file

@ -0,0 +1,11 @@
library;
import self as self;
static method method1(dynamic a) → dynamic
;
static method method2() → dynamic
;
static method method3() → dynamic async
;
static method method4() → dynamic async
;

View file

@ -0,0 +1,67 @@
library;
//
// Problems in library:
//
// pkg/front_end/testcases/general/invalid_yield.dart:6:3: Error: 'yield' can only be used in 'sync*' or 'async*' methods.
// yield null;
// ^^^^^
//
// pkg/front_end/testcases/general/invalid_yield.dart:7:3: Error: 'yield' isn't a type.
// yield a;
// ^^^^^
//
// pkg/front_end/testcases/general/invalid_yield.dart:8:3: Error: 'yield' can only be used in 'sync*' or 'async*' methods.
// yield a();
// ^^^^^
//
// pkg/front_end/testcases/general/invalid_yield.dart:9:3: Error: Expected ';' after this.
// yield 1;
// ^^^^^
//
// pkg/front_end/testcases/general/invalid_yield.dart:9:3: Error: Undefined name 'yield'.
// yield 1;
// ^^^^^
//
// pkg/front_end/testcases/general/invalid_yield.dart:13:3: Error: Undefined name 'yield'.
// yield* [1];
// ^^^^^
//
// pkg/front_end/testcases/general/invalid_yield.dart:17:3: Error: 'yield' can only be used in 'sync*' or 'async*' methods.
// yield 1;
// ^^^^^
//
// pkg/front_end/testcases/general/invalid_yield.dart:21:3: Error: 'yield' can only be used in 'sync*' or 'async*' methods.
// yield [1];
// ^^^^^
//
import self as self;
import "dart:core" as core;
static method method1(dynamic a) → dynamic {
invalid-expression "pkg/front_end/testcases/general/invalid_yield.dart:6:3: Error: 'yield' can only be used in 'sync*' or 'async*' methods.
yield null;
^";
invalid-type a;
invalid-expression "pkg/front_end/testcases/general/invalid_yield.dart:8:3: Error: 'yield' can only be used in 'sync*' or 'async*' methods.
yield a();
^";
invalid-expression "pkg/front_end/testcases/general/invalid_yield.dart:9:3: Error: Undefined name 'yield'.
yield 1;
^^^^^";
1;
}
static method method2() → dynamic {
invalid-expression "pkg/front_end/testcases/general/invalid_yield.dart:13:3: Error: Undefined name 'yield'.
yield* [1];
^^^^^"{<invalid>}.*(core::_GrowableList::_literal1<core::int>(1));
}
static method method3() → dynamic async /* futureValueType= dynamic */ {
invalid-expression "pkg/front_end/testcases/general/invalid_yield.dart:17:3: Error: 'yield' can only be used in 'sync*' or 'async*' methods.
yield 1;
^";
}
static method method4() → dynamic async /* futureValueType= dynamic */ {
invalid-expression "pkg/front_end/testcases/general/invalid_yield.dart:21:3: Error: 'yield' can only be used in 'sync*' or 'async*' methods.
yield [1];
^";
}