Analyzer: Fix representation of an arrow generator (illegal) function
Fixes https://github.com/dart-lang/sdk/issues/43665
A function like `foo() sync* => []` is illegal, but there is no _parse_
error, only a compile-time error. So the analyzer needs to be able to
represent this function accurately.
I think that without this fix, the formatter crashes trying to format
such a function.
Without this fix, a function like `foo() sync* => []` has a
FunctionElement with `isAsynchronous` true (because there _is_ a
modifier) and `isGenerator` false, because an arrow function cannot be
a generator. But these bool values lead to wrong errors being reported.
Change-Id: I1f7bda3696d04b437f6ea12491ca4aa19b893574
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/210820
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Samuel Rawlins <srawlins@google.com>
diff --git a/pkg/analyzer/lib/dart/ast/ast_factory.dart b/pkg/analyzer/lib/dart/ast/ast_factory.dart
index 15022fc..d8469f2 100644
--- a/pkg/analyzer/lib/dart/ast/ast_factory.dart
+++ b/pkg/analyzer/lib/dart/ast/ast_factory.dart
@@ -323,9 +323,22 @@
/// Returns a newly created function body consisting of an expression.
/// The [keyword] can be `null` if the function body is not an async function
/// body.
+ @Deprecated("Use expressionFunctionBody2, with new 'star' parameter")
ExpressionFunctionBody expressionFunctionBody(Token? keyword,
Token functionDefinition, Expression expression, Token? semicolon);
+ /// Returns a newly created function body consisting of an expression.
+ /// The [keyword] can be `null` if the function body is not an async function
+ /// body. The [star] can be `null` if there is no star following the keyword
+ /// (and must be `null` if there is no keyword).
+ ExpressionFunctionBody expressionFunctionBody2({
+ Token? keyword,
+ Token? star,
+ required Token functionDefinition,
+ required Expression expression,
+ Token? semicolon,
+ });
+
/// Returns a newly created expression statement.
ExpressionStatement expressionStatement(
Expression expression, Token? semicolon);
diff --git a/pkg/analyzer/lib/src/dart/ast/ast.dart b/pkg/analyzer/lib/src/dart/ast/ast.dart
index 434b963..0ea9f43 100644
--- a/pkg/analyzer/lib/src/dart/ast/ast.dart
+++ b/pkg/analyzer/lib/src/dart/ast/ast.dart
@@ -3381,6 +3381,14 @@
@override
Token? keyword;
+ /// The star optionally following the 'async' or 'sync' keyword, or `null` if
+ /// there is wither no such keyword or no star.
+ ///
+ /// It is an error for an expression function body to feature the star, but
+ /// the parser will accept it.
+ @override
+ Token? star;
+
/// The token introducing the expression that represents the body of the
/// function.
@override
@@ -3396,8 +3404,8 @@
/// Initialize a newly created function body consisting of a block of
/// statements. The [keyword] can be `null` if the function body is not an
/// async function body.
- ExpressionFunctionBodyImpl(
- this.keyword, this.functionDefinition, this._expression, this.semicolon) {
+ ExpressionFunctionBodyImpl(this.keyword, this.star, this.functionDefinition,
+ this._expression, this.semicolon) {
_becomeParentOf(_expression);
}
@@ -3412,6 +3420,7 @@
@override
Iterable<SyntacticEntity> get childEntities => ChildEntities()
..add(keyword)
+ ..add(star)
..add(functionDefinition)
..add(_expression)
..add(semicolon);
@@ -3432,10 +3441,13 @@
}
@override
- bool get isAsynchronous => keyword != null;
+ bool get isAsynchronous => keyword?.lexeme == Keyword.ASYNC.lexeme;
@override
- bool get isSynchronous => keyword == null;
+ bool get isGenerator => star != null;
+
+ @override
+ bool get isSynchronous => keyword?.lexeme != Keyword.ASYNC.lexeme;
@override
E? accept<E>(AstVisitor<E> visitor) =>
diff --git a/pkg/analyzer/lib/src/dart/ast/ast_factory.dart b/pkg/analyzer/lib/src/dart/ast/ast_factory.dart
index cc2e492..bc7287e 100644
--- a/pkg/analyzer/lib/src/dart/ast/ast_factory.dart
+++ b/pkg/analyzer/lib/src/dart/ast/ast_factory.dart
@@ -413,8 +413,19 @@
@override
ExpressionFunctionBodyImpl expressionFunctionBody(Token? keyword,
Token functionDefinition, Expression expression, Token? semicolon) =>
- ExpressionFunctionBodyImpl(
- keyword, functionDefinition, expression as ExpressionImpl, semicolon);
+ ExpressionFunctionBodyImpl(keyword, null, functionDefinition,
+ expression as ExpressionImpl, semicolon);
+
+ @override
+ ExpressionFunctionBodyImpl expressionFunctionBody2({
+ Token? keyword,
+ Token? star,
+ required Token functionDefinition,
+ required Expression expression,
+ Token? semicolon,
+ }) =>
+ ExpressionFunctionBodyImpl(keyword, star, functionDefinition,
+ expression as ExpressionImpl, semicolon);
@override
ExpressionStatementImpl expressionStatement(
diff --git a/pkg/analyzer/lib/src/dart/ast/to_source_visitor.dart b/pkg/analyzer/lib/src/dart/ast/to_source_visitor.dart
index d8eaf00..b5929ae 100644
--- a/pkg/analyzer/lib/src/dart/ast/to_source_visitor.dart
+++ b/pkg/analyzer/lib/src/dart/ast/to_source_visitor.dart
@@ -333,6 +333,9 @@
var keyword = node.keyword;
if (keyword != null) {
sink.write(keyword.lexeme);
+ if (node.star != null) {
+ sink.write('*');
+ }
sink.write(' ');
}
sink.write('${node.functionDefinition.lexeme} ');
diff --git a/pkg/analyzer/lib/src/fasta/ast_builder.dart b/pkg/analyzer/lib/src/fasta/ast_builder.dart
index 873ca31..44ea8bb 100644
--- a/pkg/analyzer/lib/src/fasta/ast_builder.dart
+++ b/pkg/analyzer/lib/src/fasta/ast_builder.dart
@@ -2665,11 +2665,16 @@
debugEvent("ExpressionFunctionBody");
var expression = pop() as Expression;
- pop(); // star (*)
+ var star = pop() as Token?;
var asyncKeyword = pop() as Token?;
if (parseFunctionBodies) {
- push(ast.expressionFunctionBody(
- asyncKeyword, arrowToken, expression, semicolon));
+ push(ast.expressionFunctionBody2(
+ keyword: asyncKeyword,
+ star: star,
+ functionDefinition: arrowToken,
+ expression: expression,
+ semicolon: semicolon,
+ ));
} else {
push(ast.emptyFunctionBody(semicolon!));
}
diff --git a/pkg/analyzer/lib/src/generated/testing/ast_test_factory.dart b/pkg/analyzer/lib/src/generated/testing/ast_test_factory.dart
index 70d878c..71aab4d 100644
--- a/pkg/analyzer/lib/src/generated/testing/ast_test_factory.dart
+++ b/pkg/analyzer/lib/src/generated/testing/ast_test_factory.dart
@@ -93,11 +93,14 @@
static ExpressionFunctionBodyImpl asyncExpressionFunctionBody(
Expression expression) =>
- astFactory.expressionFunctionBody(
- TokenFactory.tokenFromTypeAndString(TokenType.IDENTIFIER, "async"),
- TokenFactory.tokenFromType(TokenType.FUNCTION),
- expression,
- TokenFactory.tokenFromType(TokenType.SEMICOLON));
+ astFactory.expressionFunctionBody2(
+ keyword:
+ TokenFactory.tokenFromTypeAndString(TokenType.IDENTIFIER, "async"),
+ star: null,
+ functionDefinition: TokenFactory.tokenFromType(TokenType.FUNCTION),
+ expression: expression,
+ semicolon: TokenFactory.tokenFromType(TokenType.SEMICOLON),
+ );
static BlockFunctionBodyImpl asyncGeneratorBlockFunctionBody(
[List<Statement> statements = const []]) =>
@@ -106,6 +109,17 @@
TokenFactory.tokenFromType(TokenType.STAR),
block(statements));
+ static ExpressionFunctionBodyImpl asyncGeneratorExpressionFunctionBody(
+ Expression expression) =>
+ astFactory.expressionFunctionBody2(
+ keyword:
+ TokenFactory.tokenFromTypeAndString(TokenType.IDENTIFIER, "async"),
+ star: TokenFactory.tokenFromType(TokenType.STAR),
+ functionDefinition: TokenFactory.tokenFromType(TokenType.FUNCTION),
+ expression: expression,
+ semicolon: TokenFactory.tokenFromType(TokenType.SEMICOLON),
+ );
+
static AwaitExpressionImpl awaitExpression(Expression expression) =>
astFactory.awaitExpression(
TokenFactory.tokenFromTypeAndString(TokenType.IDENTIFIER, "await"),
@@ -485,11 +499,13 @@
static ExpressionFunctionBodyImpl expressionFunctionBody(
Expression expression) =>
- astFactory.expressionFunctionBody(
- null,
- TokenFactory.tokenFromType(TokenType.FUNCTION),
- expression,
- TokenFactory.tokenFromType(TokenType.SEMICOLON));
+ astFactory.expressionFunctionBody2(
+ keyword: null,
+ star: null,
+ functionDefinition: TokenFactory.tokenFromType(TokenType.FUNCTION),
+ expression: expression,
+ semicolon: TokenFactory.tokenFromType(TokenType.SEMICOLON),
+ );
static ExpressionStatementImpl expressionStatement(Expression expression) =>
astFactory.expressionStatement(
diff --git a/pkg/analyzer/test/src/dart/ast/to_source_visitor_test.dart b/pkg/analyzer/test/src/dart/ast/to_source_visitor_test.dart
index 6119dbf..88e81b9 100644
--- a/pkg/analyzer/test/src/dart/ast/to_source_visitor_test.dart
+++ b/pkg/analyzer/test/src/dart/ast/to_source_visitor_test.dart
@@ -826,6 +826,13 @@
AstTestFactory.identifier3("a")));
}
+ void test_visitExpressionFunctionBody_async_star() {
+ _assertSource(
+ "async* => a;",
+ AstTestFactory.asyncGeneratorExpressionFunctionBody(
+ AstTestFactory.identifier3("a")));
+ }
+
void test_visitExpressionFunctionBody_simple() {
_assertSource("=> a;",
AstTestFactory.expressionFunctionBody(AstTestFactory.identifier3("a")));
diff --git a/pkg/analyzer/test/src/diagnostics/illegal_sync_generator_return_type_test.dart b/pkg/analyzer/test/src/diagnostics/illegal_sync_generator_return_type_test.dart
index 1656f94..d22ab61 100644
--- a/pkg/analyzer/test/src/diagnostics/illegal_sync_generator_return_type_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/illegal_sync_generator_return_type_test.dart
@@ -15,6 +15,14 @@
@reflectiveTest
class IllegalSyncGeneratorReturnTypeTest extends PubPackageResolutionTest {
+ test_arrowFunction_iterator() async {
+ await assertErrorsInCode('''
+Iterable<void> f() sync* => [];
+''', [
+ error(CompileTimeErrorCode.RETURN_IN_GENERATOR, 25, 2),
+ ]);
+ }
+
test_function_iterator() async {
await assertNoErrorsInCode('''
Iterable<void> f() sync* {}