Add summary support for "for each" elements with declarations.
Change-Id: I9bdc8027ce002e87e6a34b3e306f6ef27bbfac59
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/97444
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/src/summary/expr_builder.dart b/pkg/analyzer/lib/src/summary/expr_builder.dart
index 0447c55..2fe2012 100644
--- a/pkg/analyzer/lib/src/summary/expr_builder.dart
+++ b/pkg/analyzer/lib/src/summary/expr_builder.dart
@@ -362,6 +362,12 @@
case UnlinkedExprOperation.forEachPartsWithIdentifier:
_forEachPartsWithIdentifier();
break;
+ case UnlinkedExprOperation.forEachPartsWithUntypedDeclaration:
+ _forEachPartsWithDeclaration(false);
+ break;
+ case UnlinkedExprOperation.forEachPartsWithTypedDeclaration:
+ _forEachPartsWithDeclaration(true);
+ break;
case UnlinkedExprOperation.cascadeSectionBegin:
case UnlinkedExprOperation.cascadeSectionEnd:
case UnlinkedExprOperation.pushLocalFunctionReference:
@@ -622,6 +628,22 @@
return _buildIdentifierSequence(info);
}
+ void _forEachPartsWithDeclaration(bool hasType) {
+ var iterable = _pop();
+ var name = _uc.strings[stringPtr++];
+ var element = LocalVariableElementImpl(name, -1);
+ var keyword = hasType ? null : Keyword.VAR;
+ var type = hasType ? _newTypeName() : null;
+ var loopVariable = AstTestFactory.declaredIdentifier2(keyword, type, name);
+ loopVariable.identifier.staticElement = element;
+ if (hasType) {
+ element.type = type.type;
+ }
+ _pushNode(
+ AstTestFactory.forEachPartsWithDeclaration(loopVariable, iterable));
+ variablesInScope.push(element);
+ }
+
void _forEachPartsWithIdentifier() {
var iterable = _pop();
SimpleIdentifier identifier = _pop();
@@ -715,6 +737,8 @@
var forLoopParts = _popNode() as ForLoopParts;
if (forLoopParts is ForPartsWithDeclarations) {
variablesInScope.pop(forLoopParts.variables.variables.length);
+ } else if (forLoopParts is ForEachPartsWithDeclaration) {
+ variablesInScope.pop(1);
}
_pushCollectionElement(
AstTestFactory.forElement(forLoopParts, body, hasAwait: hasAwait));
diff --git a/pkg/analyzer/lib/src/summary/format.fbs b/pkg/analyzer/lib/src/summary/format.fbs
index 554d4b5..3d529d9 100644
--- a/pkg/analyzer/lib/src/summary/format.fbs
+++ b/pkg/analyzer/lib/src/summary/format.fbs
@@ -1049,7 +1049,18 @@
/// Pop the top 2 values from the stack. The first is the for loop parts.
/// The second is the body.
- forElementWithAwait
+ forElementWithAwait,
+
+ /// Pop an expression from the stack, and create for-each parts of the form
+ /// `var name in expression`, where `name` is obtained from
+ /// [UnlinkedExpr.strings].
+ forEachPartsWithUntypedDeclaration,
+
+ /// Pop an expression from the stack, and create for-each parts of the form
+ /// `Type name in expression`, where `name` is obtained from
+ /// [UnlinkedExpr.strings], and `Type` is obtained from
+ /// [UnlinkedExpr.references].
+ forEachPartsWithTypedDeclaration
}
/// 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 03a5457..81731be 100644
--- a/pkg/analyzer/lib/src/summary/idl.dart
+++ b/pkg/analyzer/lib/src/summary/idl.dart
@@ -3962,6 +3962,17 @@
/// Pop the top 2 values from the stack. The first is the for loop parts.
/// The second is the body.
forElementWithAwait,
+
+ /// Pop an expression from the stack, and create for-each parts of the form
+ /// `var name in expression`, where `name` is obtained from
+ /// [UnlinkedExpr.strings].
+ forEachPartsWithUntypedDeclaration,
+
+ /// Pop an expression from the stack, and create for-each parts of the form
+ /// `Type name in expression`, where `name` is obtained from
+ /// [UnlinkedExpr.strings], and `Type` is obtained from
+ /// [UnlinkedExpr.references].
+ forEachPartsWithTypedDeclaration,
}
/// Unlinked summary information about an import declaration.
diff --git a/pkg/analyzer/lib/src/summary/link.dart b/pkg/analyzer/lib/src/summary/link.dart
index 6898e4a..df84918 100644
--- a/pkg/analyzer/lib/src/summary/link.dart
+++ b/pkg/analyzer/lib/src/summary/link.dart
@@ -1913,6 +1913,7 @@
intPtr++;
break;
case UnlinkedExprOperation.assignToRef:
+ case UnlinkedExprOperation.forEachPartsWithTypedDeclaration:
refPtr++;
break;
case UnlinkedExprOperation.invokeMethodRef:
@@ -5255,6 +5256,7 @@
break;
case UnlinkedExprOperation.typeCast:
case UnlinkedExprOperation.typeCheck:
+ case UnlinkedExprOperation.forEachPartsWithTypedDeclaration:
refPtr++;
break;
case UnlinkedExprOperation.pushLocalFunctionReference:
diff --git a/pkg/analyzer/lib/src/summary/summarize_const_expr.dart b/pkg/analyzer/lib/src/summary/summarize_const_expr.dart
index b6dbe1c..8727ef2 100644
--- a/pkg/analyzer/lib/src/summary/summarize_const_expr.dart
+++ b/pkg/analyzer/lib/src/summary/summarize_const_expr.dart
@@ -612,9 +612,23 @@
_serialize(parts.identifier);
_serialize(parts.iterable);
operations.add(UnlinkedExprOperation.forEachPartsWithIdentifier);
+ } else if (parts is ForEachPartsWithDeclaration) {
+ _serialize(parts.iterable);
+ var type = parts.loopVariable.type;
+ if (type == null) {
+ operations
+ .add(UnlinkedExprOperation.forEachPartsWithUntypedDeclaration);
+ } else {
+ references.add(serializeType(type));
+ operations
+ .add(UnlinkedExprOperation.forEachPartsWithTypedDeclaration);
+ }
+ var name = parts.loopVariable.identifier.name;
+ strings.add(name);
+ pushVariableName(name);
+ ++numVariablesToPop;
} else {
- // See https://github.com/dart-lang/sdk/issues/35569
- throw new StateError('TODO(paulberry)');
+ throw StateError('Unrecognized for parts');
}
} else {
throw StateError('Unrecognized for parts');
diff --git a/pkg/analyzer/test/src/summary/expr_builder_test.dart b/pkg/analyzer/test/src/summary/expr_builder_test.dart
index ca5e9b4..5a8d608 100644
--- a/pkg/analyzer/test/src/summary/expr_builder_test.dart
+++ b/pkg/analyzer/test/src/summary/expr_builder_test.dart
@@ -303,6 +303,34 @@
expectedText: expectedText, extraDeclarations: 'int i;');
}
+ void test_list_for_each_with_declaration_typed() {
+ experimentStatus = ExperimentStatus(control_flow_collections: true);
+ var sourceText = '[for (int i in const []) i]';
+ // Resynthesis inserts synthetic "const" tokens; work around that.
+ var expectedText = 'const $sourceText';
+ var list = checkSimpleExpression(sourceText, expectedText: expectedText)
+ as ListLiteral;
+ var forElement = list.elements2[0] as ForElement;
+ var loopParts = forElement.forLoopParts as ForEachPartsWithDeclaration;
+ var iElement = loopParts.loopVariable.identifier.staticElement;
+ var iRef = forElement.body as SimpleIdentifier;
+ expect(iRef.staticElement, same(iElement));
+ }
+
+ void test_list_for_each_with_declaration_untyped() {
+ experimentStatus = ExperimentStatus(control_flow_collections: true);
+ var sourceText = '[for (var i in const []) i]';
+ // Resynthesis inserts synthetic "const" tokens; work around that.
+ var expectedText = 'const $sourceText';
+ var list = checkSimpleExpression(sourceText, expectedText: expectedText)
+ as ListLiteral;
+ var forElement = list.elements2[0] as ForElement;
+ var loopParts = forElement.forLoopParts as ForEachPartsWithDeclaration;
+ var iElement = loopParts.loopVariable.identifier.staticElement;
+ var iRef = forElement.body as SimpleIdentifier;
+ expect(iRef.staticElement, same(iElement));
+ }
+
void test_list_for_each_with_identifier() {
experimentStatus = ExperimentStatus(control_flow_collections: true);
var sourceText = '[for (i in const []) i]';
diff --git a/pkg/analyzer/test/src/summary/summary_common.dart b/pkg/analyzer/test/src/summary/summary_common.dart
index 9a374be..f594ee2 100644
--- a/pkg/analyzer/test/src/summary/summary_common.dart
+++ b/pkg/analyzer/test/src/summary/summary_common.dart
@@ -7692,6 +7692,57 @@
]);
}
+ test_expr_list_for_each_with_declaration_typed() {
+ experimentStatus = ExperimentStatus(
+ control_flow_collections: true, spread_collections: true);
+ UnlinkedVariable variable =
+ serializeVariableText('var v = [for (int i in []) i];');
+ assertUnlinkedConst(variable.initializer.bodyExpr, '[for (int i in []) i]',
+ isValidConst: false,
+ operators: [
+ UnlinkedExprOperation.makeUntypedList,
+ UnlinkedExprOperation.forEachPartsWithTypedDeclaration,
+ UnlinkedExprOperation.pushParameter,
+ UnlinkedExprOperation.forElement,
+ UnlinkedExprOperation.makeUntypedList
+ ],
+ ints: [
+ 0,
+ 1
+ ],
+ strings: [
+ 'i',
+ 'i'
+ ],
+ referenceValidators: [
+ (EntityRef r) => checkTypeRef(r, 'dart:core', 'int')
+ ]);
+ }
+
+ test_expr_list_for_each_with_declaration_untyped() {
+ experimentStatus = ExperimentStatus(
+ control_flow_collections: true, spread_collections: true);
+ UnlinkedVariable variable =
+ serializeVariableText('var v = [for (var i in []) i];');
+ assertUnlinkedConst(variable.initializer.bodyExpr, '[for (var i in []) i]',
+ isValidConst: false,
+ operators: [
+ UnlinkedExprOperation.makeUntypedList,
+ UnlinkedExprOperation.forEachPartsWithUntypedDeclaration,
+ UnlinkedExprOperation.pushParameter,
+ UnlinkedExprOperation.forElement,
+ UnlinkedExprOperation.makeUntypedList
+ ],
+ ints: [
+ 0,
+ 1
+ ],
+ strings: [
+ 'i',
+ 'i'
+ ]);
+ }
+
test_expr_list_for_each_with_identifier() {
experimentStatus = ExperimentStatus(
control_flow_collections: true, spread_collections: true);