Add summary support for "for each" elements with identifiers.
Change-Id: Id75fbf13d2d7063ff1e3e0122f801321adf0ef89
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/97442
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
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 80779ea..c62b040 100644
--- a/pkg/analyzer/lib/src/generated/testing/ast_test_factory.dart
+++ b/pkg/analyzer/lib/src/generated/testing/ast_test_factory.dart
@@ -509,8 +509,11 @@
iterable: iterable);
static ForElement forElement(
- ForLoopParts forLoopParts, CollectionElement body) =>
+ ForLoopParts forLoopParts, CollectionElement body,
+ {bool hasAwait: false}) =>
astFactory.forElement(
+ awaitKeyword:
+ hasAwait ? TokenFactory.tokenFromKeyword(Keyword.AWAIT) : null,
forKeyword: TokenFactory.tokenFromKeyword(Keyword.FOR),
leftParenthesis: TokenFactory.tokenFromType(TokenType.OPEN_PAREN),
forLoopParts: forLoopParts,
diff --git a/pkg/analyzer/lib/src/summary/expr_builder.dart b/pkg/analyzer/lib/src/summary/expr_builder.dart
index 694ca03..0447c55 100644
--- a/pkg/analyzer/lib/src/summary/expr_builder.dart
+++ b/pkg/analyzer/lib/src/summary/expr_builder.dart
@@ -333,7 +333,10 @@
_pushForParts();
break;
case UnlinkedExprOperation.forElement:
- _pushForElement();
+ _pushForElement(false);
+ break;
+ case UnlinkedExprOperation.forElementWithAwait:
+ _pushForElement(true);
break;
case UnlinkedExprOperation.pushEmptyExpression:
_push(null);
@@ -356,6 +359,9 @@
identifier.staticElement = variablesInScope[name];
_push(_createAssignment(identifier));
break;
+ case UnlinkedExprOperation.forEachPartsWithIdentifier:
+ _forEachPartsWithIdentifier();
+ break;
case UnlinkedExprOperation.cascadeSectionBegin:
case UnlinkedExprOperation.cascadeSectionEnd:
case UnlinkedExprOperation.pushLocalFunctionReference:
@@ -616,6 +622,12 @@
return _buildIdentifierSequence(info);
}
+ void _forEachPartsWithIdentifier() {
+ var iterable = _pop();
+ SimpleIdentifier identifier = _pop();
+ _pushNode(AstTestFactory.forEachPartsWithIdentifier(identifier, iterable));
+ }
+
void _forInitializerDeclarations(bool hasType) {
var count = _uc.ints[intPtr++];
var variables = List<VariableDeclaration>.filled(count, null);
@@ -698,13 +710,14 @@
_push(AstTestFactory.propertyAccess(target, propertyNode));
}
- void _pushForElement() {
+ void _pushForElement(bool hasAwait) {
var body = _popCollectionElement();
var forLoopParts = _popNode() as ForLoopParts;
if (forLoopParts is ForPartsWithDeclarations) {
variablesInScope.pop(forLoopParts.variables.variables.length);
}
- _pushCollectionElement(AstTestFactory.forElement(forLoopParts, body));
+ _pushCollectionElement(
+ AstTestFactory.forElement(forLoopParts, body, hasAwait: hasAwait));
}
void _pushForParts() {
diff --git a/pkg/analyzer/lib/src/summary/format.fbs b/pkg/analyzer/lib/src/summary/format.fbs
index 4cb2a21..554d4b5 100644
--- a/pkg/analyzer/lib/src/summary/format.fbs
+++ b/pkg/analyzer/lib/src/summary/format.fbs
@@ -1041,7 +1041,15 @@
/// `value` is not present in the stack, so it should not be popped and the
/// corresponding value of the parameter after/before update is pushed onto
/// the stack instead.
- assignToParameter
+ assignToParameter,
+
+ /// Pop from the stack an identifier and an expression, and create for-each
+ /// parts of the form `identifier in expression`.
+ forEachPartsWithIdentifier,
+
+ /// Pop the top 2 values from the stack. The first is the for loop parts.
+ /// The second is the body.
+ forElementWithAwait
}
/// Enum used to indicate the kind of a parameter.
diff --git a/pkg/analyzer/lib/src/summary/idl.dart b/pkg/analyzer/lib/src/summary/idl.dart
index fd75a96..03a5457 100644
--- a/pkg/analyzer/lib/src/summary/idl.dart
+++ b/pkg/analyzer/lib/src/summary/idl.dart
@@ -3954,6 +3954,14 @@
/// corresponding value of the parameter after/before update is pushed onto
/// the stack instead.
assignToParameter,
+
+ /// Pop from the stack an identifier and an expression, and create for-each
+ /// parts of the form `identifier in expression`.
+ forEachPartsWithIdentifier,
+
+ /// Pop the top 2 values from the stack. The first is the for loop parts.
+ /// The second is the body.
+ forElementWithAwait,
}
/// Unlinked summary information about an import declaration.
diff --git a/pkg/analyzer/lib/src/summary/summarize_const_expr.dart b/pkg/analyzer/lib/src/summary/summarize_const_expr.dart
index ec233dd..b6dbe1c 100644
--- a/pkg/analyzer/lib/src/summary/summarize_const_expr.dart
+++ b/pkg/analyzer/lib/src/summary/summarize_const_expr.dart
@@ -571,6 +571,7 @@
operations.add(UnlinkedExprOperation.ifElseElement);
}
} else if (element is ForElement) {
+ isValidConst = false;
var parts = element.forLoopParts;
int numVariablesToPop = 0;
if (parts is ForParts) {
@@ -606,13 +607,23 @@
}
operations.add(UnlinkedExprOperation.forParts);
ints.add(parts.updaters.length);
+ } else if (parts is ForEachParts) {
+ if (parts is ForEachPartsWithIdentifier) {
+ _serialize(parts.identifier);
+ _serialize(parts.iterable);
+ operations.add(UnlinkedExprOperation.forEachPartsWithIdentifier);
+ } else {
+ // See https://github.com/dart-lang/sdk/issues/35569
+ throw new StateError('TODO(paulberry)');
+ }
} else {
- // See https://github.com/dart-lang/sdk/issues/35569
- throw new StateError('TODO(paulberry)');
+ throw StateError('Unrecognized for parts');
}
_serializeCollectionElement(element.body);
popVariableNames(numVariablesToPop);
- operations.add(UnlinkedExprOperation.forElement);
+ operations.add(element.awaitKeyword == null
+ ? UnlinkedExprOperation.forElement
+ : UnlinkedExprOperation.forElementWithAwait);
} else {
throw new StateError('Unsupported CollectionElement: $element');
}
diff --git a/pkg/analyzer/test/src/summary/expr_builder_test.dart b/pkg/analyzer/test/src/summary/expr_builder_test.dart
index 5898a3f..ca5e9b4 100644
--- a/pkg/analyzer/test/src/summary/expr_builder_test.dart
+++ b/pkg/analyzer/test/src/summary/expr_builder_test.dart
@@ -303,6 +303,24 @@
expectedText: expectedText, extraDeclarations: 'int i;');
}
+ void test_list_for_each_with_identifier() {
+ experimentStatus = ExperimentStatus(control_flow_collections: true);
+ var sourceText = '[for (i in const []) i]';
+ // Resynthesis inserts synthetic "const" tokens; work around that.
+ var expectedText = 'const $sourceText';
+ checkSimpleExpression(sourceText,
+ expectedText: expectedText, extraDeclarations: 'int i;');
+ }
+
+ void test_list_for_each_with_identifier_await() {
+ experimentStatus = ExperimentStatus(control_flow_collections: true);
+ var sourceText = '[await for (i in const []) i]';
+ // Resynthesis inserts synthetic "const" tokens; work around that.
+ var expectedText = 'const $sourceText';
+ checkSimpleExpression(sourceText,
+ expectedText: expectedText, extraDeclarations: 'int i;');
+ }
+
void test_list_for_empty_condition() {
experimentStatus = ExperimentStatus(control_flow_collections: true);
var sourceText = '[for (i = 0;; i++) i]';
diff --git a/pkg/analyzer/test/src/summary/summary_common.dart b/pkg/analyzer/test/src/summary/summary_common.dart
index 91d5349..9a374be 100644
--- a/pkg/analyzer/test/src/summary/summary_common.dart
+++ b/pkg/analyzer/test/src/summary/summary_common.dart
@@ -7692,6 +7692,61 @@
]);
}
+ test_expr_list_for_each_with_identifier() {
+ experimentStatus = ExperimentStatus(
+ control_flow_collections: true, spread_collections: true);
+ UnlinkedVariable variable =
+ serializeVariableText('int i; var v = [for (i in []) i];');
+ assertUnlinkedConst(variable.initializer.bodyExpr, '[for (i in []) i]',
+ isValidConst: false,
+ operators: [
+ UnlinkedExprOperation.pushReference,
+ UnlinkedExprOperation.makeUntypedList,
+ UnlinkedExprOperation.forEachPartsWithIdentifier,
+ UnlinkedExprOperation.pushReference,
+ UnlinkedExprOperation.forElement,
+ UnlinkedExprOperation.makeUntypedList
+ ],
+ ints: [
+ 0,
+ 1
+ ],
+ referenceValidators: [
+ (EntityRef r) => checkTypeRef(r, null, 'i',
+ expectedKind: ReferenceKind.topLevelPropertyAccessor),
+ (EntityRef r) => checkTypeRef(r, null, 'i',
+ expectedKind: ReferenceKind.topLevelPropertyAccessor)
+ ]);
+ }
+
+ test_expr_list_for_each_with_identifier_await() {
+ experimentStatus = ExperimentStatus(
+ control_flow_collections: true, spread_collections: true);
+ UnlinkedVariable variable =
+ serializeVariableText('int i; var v = [await for (i in []) i];');
+ assertUnlinkedConst(
+ variable.initializer.bodyExpr, '[await for (i in []) i]',
+ isValidConst: false,
+ operators: [
+ UnlinkedExprOperation.pushReference,
+ UnlinkedExprOperation.makeUntypedList,
+ UnlinkedExprOperation.forEachPartsWithIdentifier,
+ UnlinkedExprOperation.pushReference,
+ UnlinkedExprOperation.forElementWithAwait,
+ UnlinkedExprOperation.makeUntypedList
+ ],
+ ints: [
+ 0,
+ 1
+ ],
+ referenceValidators: [
+ (EntityRef r) => checkTypeRef(r, null, 'i',
+ expectedKind: ReferenceKind.topLevelPropertyAccessor),
+ (EntityRef r) => checkTypeRef(r, null, 'i',
+ expectedKind: ReferenceKind.topLevelPropertyAccessor)
+ ]);
+ }
+
test_expr_list_for_empty_condition() {
experimentStatus = ExperimentStatus(
control_flow_collections: true, spread_collections: true);