parse nested control flow collection entries
Change-Id: Id50c0a53a51f076dd75dd5bc13a694663449bc83
Reviewed-on: https://dart-review.googlesource.com/c/91400
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analysis_server/test/analysis/notification_highlights2_test.dart b/pkg/analysis_server/test/analysis/notification_highlights2_test.dart
index a6c75c5..4aa6ddc 100644
--- a/pkg/analysis_server/test/analysis/notification_highlights2_test.dart
+++ b/pkg/analysis_server/test/analysis/notification_highlights2_test.dart
@@ -1121,6 +1121,11 @@
}
void processNotification(Notification notification) {
+ if (notification.event == SERVER_NOTIFICATION_ERROR) {
+ print('SERVER_NOTIFICATION_ERROR: ${notification.toJson()}');
+ _resultsAvailable.complete(null);
+ fail('SERVER_NOTIFICATION_ERROR');
+ }
if (notification.event == ANALYSIS_NOTIFICATION_HIGHLIGHTS) {
var params = new AnalysisHighlightsParams.fromNotification(notification);
if (params.file == testFile) {
diff --git a/pkg/analysis_server/test/analysis/notification_highlights_test.dart b/pkg/analysis_server/test/analysis/notification_highlights_test.dart
index 9e1b109..b992bc9 100644
--- a/pkg/analysis_server/test/analysis/notification_highlights_test.dart
+++ b/pkg/analysis_server/test/analysis/notification_highlights_test.dart
@@ -971,6 +971,11 @@
}
void processNotification(Notification notification) {
+ if (notification.event == SERVER_NOTIFICATION_ERROR) {
+ print('SERVER_NOTIFICATION_ERROR: ${notification.toJson()}');
+ _resultsAvailable.complete(null);
+ fail('SERVER_NOTIFICATION_ERROR');
+ }
if (notification.event == ANALYSIS_NOTIFICATION_HIGHLIGHTS) {
var params = new AnalysisHighlightsParams.fromNotification(notification);
if (params.file == testFile) {
diff --git a/pkg/analyzer/lib/src/dart/ast/ast.dart b/pkg/analyzer/lib/src/dart/ast/ast.dart
index 228a0fa..d6abf5e 100644
--- a/pkg/analyzer/lib/src/dart/ast/ast.dart
+++ b/pkg/analyzer/lib/src/dart/ast/ast.dart
@@ -7987,7 +7987,7 @@
abstract class MapElementImpl extends AstNodeImpl implements MapElement {}
-class MapForElementImpl extends CollectionElementImpl
+class MapForElementImpl extends MapElementImpl
with ForMixin
implements MapForElement {
/**
diff --git a/pkg/analyzer/lib/src/fasta/ast_builder.dart b/pkg/analyzer/lib/src/fasta/ast_builder.dart
index 5cab0db..0413025 100644
--- a/pkg/analyzer/lib/src/fasta/ast_builder.dart
+++ b/pkg/analyzer/lib/src/fasta/ast_builder.dart
@@ -3288,28 +3288,28 @@
this.forLoopParts, this.rightParenthesis, this.entry);
@override
- CollectionElement asCollectionElement(AstFactory ast) {
- return ast.collectionForElement(
- awaitKeyword: awaitToken,
- forKeyword: forKeyword,
- leftParenthesis: leftParenthesis,
- forLoopParts: forLoopParts,
- rightParenthesis: rightParenthesis,
- body: entry,
- );
- }
+ CollectionElement asCollectionElement(AstFactory ast) =>
+ ast.collectionForElement(
+ awaitKeyword: awaitToken,
+ forKeyword: forKeyword,
+ leftParenthesis: leftParenthesis,
+ forLoopParts: forLoopParts,
+ rightParenthesis: rightParenthesis,
+ body: entry is _EntryInfo
+ ? entry.asCollectionElement(ast)
+ : entry as CollectionElement,
+ );
@override
- MapElement asMapElement(AstFactory ast) {
- return ast.mapForElement(
- awaitKeyword: awaitToken,
- forKeyword: forKeyword,
- leftParenthesis: leftParenthesis,
- forLoopParts: forLoopParts,
- rightParenthesis: rightParenthesis,
- body: entry,
- );
- }
+ MapElement asMapElement(AstFactory ast) => ast.mapForElement(
+ awaitKeyword: awaitToken,
+ forKeyword: forKeyword,
+ leftParenthesis: leftParenthesis,
+ forLoopParts: forLoopParts,
+ rightParenthesis: rightParenthesis,
+ body:
+ entry is _EntryInfo ? entry.asMapElement(ast) : entry as MapElement,
+ );
}
class _IfControlFlowInfo implements _EntryInfo {
diff --git a/pkg/analyzer/test/generated/parser_fasta_test.dart b/pkg/analyzer/test/generated/parser_fasta_test.dart
index c5b4ea7..7dbd7f4 100644
--- a/pkg/analyzer/test/generated/parser_fasta_test.dart
+++ b/pkg/analyzer/test/generated/parser_fasta_test.dart
@@ -148,6 +148,34 @@
expect(iterable.name, 'list');
}
+ void test_listLiteral_forIf() {
+ ListLiteral2 list = parseCollectionLiteral(
+ '[1, await for (var x in list) if (c) 2]',
+ inAsync: true,
+ );
+ expect(list.elements, hasLength(2));
+ IntegerLiteral first = list.elements[0];
+ expect(first.value, 1);
+
+ CollectionForElement second = list.elements[1];
+ expect(second.awaitKeyword, isNotNull);
+ expect(second.forKeyword.isKeyword, isTrue);
+ expect(second.leftParenthesis.lexeme, '(');
+ expect(second.rightParenthesis.lexeme, ')');
+ ForEachPartsWithDeclaration forLoopParts = second.forLoopParts;
+ DeclaredIdentifier forLoopVar = forLoopParts.loopVariable;
+ expect(forLoopVar.identifier.name, 'x');
+ expect(forLoopParts.inKeyword, isNotNull);
+ SimpleIdentifier iterable = forLoopParts.iterable;
+ expect(iterable.name, 'list');
+
+ CollectionIfElement body = second.body;
+ SimpleIdentifier condition = body.condition;
+ expect(condition.name, 'c');
+ IntegerLiteral thenElement = body.thenElement;
+ expect(thenElement.value, 2);
+ }
+
void test_listLiteral_forSpread() {
ListLiteral2 list =
parseCollectionLiteral('[1, for (int x = 0; x < 10; ++x) ...[2]]');
@@ -200,6 +228,46 @@
expect(elseElement.value, 5);
}
+ void test_listLiteral_ifElseFor() {
+ ListLiteral2 list =
+ parseCollectionLiteral('[1, if (true) 2 else for (a in b) 5]');
+ expect(list.elements, hasLength(2));
+ IntegerLiteral first = list.elements[0];
+ expect(first.value, 1);
+
+ CollectionIfElement second = list.elements[1];
+ BooleanLiteral condition = second.condition;
+ expect(condition.value, isTrue);
+ IntegerLiteral thenElement = second.thenElement;
+ expect(thenElement.value, 2);
+
+ CollectionForElement elseElement = second.elseElement;
+ ForEachPartsWithIdentifier forLoopParts = elseElement.forLoopParts;
+ expect(forLoopParts.identifier.name, 'a');
+
+ IntegerLiteral forValue = elseElement.body;
+ expect(forValue.value, 5);
+ }
+
+ void test_listLiteral_ifFor() {
+ ListLiteral2 list = parseCollectionLiteral('[1, if (true) for (a in b) 2]');
+ expect(list.elements, hasLength(2));
+ IntegerLiteral first = list.elements[0];
+ expect(first.value, 1);
+
+ CollectionIfElement second = list.elements[1];
+ BooleanLiteral condition = second.condition;
+ expect(condition.value, isTrue);
+
+ CollectionForElement thenElement = second.thenElement;
+ ForEachPartsWithIdentifier forLoopParts = thenElement.forLoopParts;
+ expect(forLoopParts.identifier.name, 'a');
+
+ IntegerLiteral forValue = thenElement.body;
+ expect(forValue.value, 2);
+ expect(second.elseElement, isNull);
+ }
+
void test_listLiteral_ifSpread() {
ListLiteral2 list = parseCollectionLiteral('[1, if (true) ...[2]]');
expect(list.elements, hasLength(2));
@@ -275,6 +343,35 @@
expect(iterable.name, 'list');
}
+ void test_mapLiteral_forIf() {
+ MapLiteral2 map = parseCollectionLiteral(
+ '{1:7, await for (y in list) if (c) 2:3}',
+ inAsync: true);
+ expect(map.entries, hasLength(2));
+ MapLiteralEntry first = map.entries[0];
+ IntegerLiteral firstValue = first.value;
+ expect(firstValue.value, 7);
+
+ MapForElement second = map.entries[1];
+ expect(second.awaitKeyword, isNotNull);
+ expect(second.forKeyword.isKeyword, isTrue);
+ expect(second.leftParenthesis.lexeme, '(');
+ expect(second.rightParenthesis.lexeme, ')');
+ ForEachPartsWithIdentifier forLoopParts = second.forLoopParts;
+ SimpleIdentifier forLoopVar = forLoopParts.identifier;
+ expect(forLoopVar.name, 'y');
+ expect(forLoopParts.inKeyword, isNotNull);
+ SimpleIdentifier iterable = forLoopParts.iterable;
+ expect(iterable.name, 'list');
+
+ MapIfElement body = second.body;
+ SimpleIdentifier condition = body.condition;
+ expect(condition.name, 'c');
+ MapLiteralEntry thenElement = body.thenElement;
+ IntegerLiteral thenValue = thenElement.value;
+ expect(thenValue.value, 3);
+ }
+
void test_mapLiteral_forSpread() {
MapLiteral2 map =
parseCollectionLiteral('{1:7, for (x = 0; x < 10; ++x) ...{2:3}}');
@@ -334,6 +431,52 @@
expect(elseElementValue.value, 6);
}
+ void test_mapLiteral_ifElseFor() {
+ MapLiteral2 map =
+ parseCollectionLiteral('{1:1, if (true) 2:4 else for (c in d) 5:6}');
+ expect(map.entries, hasLength(2));
+ MapLiteralEntry first = map.entries[0];
+ IntegerLiteral firstValue = first.value;
+ expect(firstValue.value, 1);
+
+ MapIfElement second = map.entries[1];
+ BooleanLiteral condition = second.condition;
+ expect(condition.value, isTrue);
+ MapLiteralEntry thenElement = second.thenElement;
+ IntegerLiteral thenElementValue = thenElement.value;
+ expect(thenElementValue.value, 4);
+
+ MapForElement elseElement = second.elseElement;
+ ForEachPartsWithIdentifier forLoopParts = elseElement.forLoopParts;
+ expect(forLoopParts.identifier.name, 'c');
+
+ MapLiteralEntry body = elseElement.body;
+ IntegerLiteral bodyValue = body.value;
+ expect(bodyValue.value, 6);
+ }
+
+ void test_mapLiteral_ifFor() {
+ MapLiteral2 map =
+ parseCollectionLiteral('{1:1, if (true) for (a in b) 2:4}');
+ expect(map.entries, hasLength(2));
+ MapLiteralEntry first = map.entries[0];
+ IntegerLiteral firstValue = first.value;
+ expect(firstValue.value, 1);
+
+ MapIfElement second = map.entries[1];
+ BooleanLiteral condition = second.condition;
+ expect(condition.value, isTrue);
+
+ MapForElement thenElement = second.thenElement;
+ ForEachPartsWithIdentifier forLoopParts = thenElement.forLoopParts;
+ expect(forLoopParts.identifier.name, 'a');
+
+ MapLiteralEntry body = thenElement.body;
+ IntegerLiteral thenElementValue = body.value;
+ expect(thenElementValue.value, 4);
+ expect(second.elseElement, isNull);
+ }
+
void test_mapLiteral_ifSpread() {
MapLiteral2 map = parseCollectionLiteral('{1:1, if (true) ...{2:4}}');
expect(map.entries, hasLength(2));
diff --git a/pkg/analyzer/test/src/dart/constant/evaluation_test.dart b/pkg/analyzer/test/src/dart/constant/evaluation_test.dart
index 088deb95..ca9e41b 100644
--- a/pkg/analyzer/test/src/dart/constant/evaluation_test.dart
+++ b/pkg/analyzer/test/src/dart/constant/evaluation_test.dart
@@ -925,9 +925,7 @@
bool get enableNewAnalysisDriver => true;
- @failingTest
test_listLiteral_nested() async {
- // Fails because we're not yet parsing nested elements.
CompilationUnit compilationUnit = await resolveSource('''
const c = [1, if (1 > 0) if (2 > 1) 2, 3];
''');
@@ -1082,9 +1080,7 @@
value.values.map((e) => e.toIntValue()), unorderedEquals([1, 2, 3, 4]));
}
- @failingTest
test_setLiteral_nested() async {
- // Fails because we're not yet parsing nested elements.
CompilationUnit compilationUnit = await resolveSource('''
const c = {1, if (1 > 0) if (2 > 1) 2, 3};
''');
diff --git a/pkg/front_end/lib/src/fasta/parser/literal_entry_info.dart b/pkg/front_end/lib/src/fasta/parser/literal_entry_info.dart
index 1af5aeb..00bf0a1 100644
--- a/pkg/front_end/lib/src/fasta/parser/literal_entry_info.dart
+++ b/pkg/front_end/lib/src/fasta/parser/literal_entry_info.dart
@@ -49,9 +49,8 @@
Token next = token.next;
if (optional('if', next)) {
return ifCondition;
- } else if (optional('for', next)) {
- return new ForCondition();
- } else if (optional('await', next) && optional('for', next.next)) {
+ } else if (optional('for', next) ||
+ (optional('await', next) && optional('for', next.next))) {
return new ForCondition();
} else if (optional('...', next) || optional('...?', next)) {
return spreadOperator;
diff --git a/pkg/front_end/lib/src/fasta/parser/literal_entry_info_impl.dart b/pkg/front_end/lib/src/fasta/parser/literal_entry_info_impl.dart
index 1c0667c..87bd6ad 100644
--- a/pkg/front_end/lib/src/fasta/parser/literal_entry_info_impl.dart
+++ b/pkg/front_end/lib/src/fasta/parser/literal_entry_info_impl.dart
@@ -57,10 +57,20 @@
@override
LiteralEntryInfo computeNext(Token token) {
Token next = token.next;
- if (optional('...', next) || optional('...?', next)) {
+ if (optional('for', next) ||
+ (optional('await', next) && optional('for', next.next))) {
+ return new Nested(
+ new ForCondition(),
+ inStyle ? const ForInComplete() : const ForComplete(),
+ );
+ } else if (optional('if', next)) {
+ return new Nested(
+ ifCondition,
+ inStyle ? const ForInComplete() : const ForComplete(),
+ );
+ } else if (optional('...', next) || optional('...?', next)) {
return inStyle ? const ForInSpread() : const ForSpread();
}
- // TODO(danrubel): nested control flow structures
return inStyle ? const ForInEntry() : const ForEntry();
}
}
@@ -144,10 +154,14 @@
@override
LiteralEntryInfo computeNext(Token token) {
Token next = token.next;
- if (optional('...', next) || optional('...?', next)) {
+ if (optional('for', next) ||
+ (optional('await', next) && optional('for', next.next))) {
+ return new Nested(new ForCondition(), const IfComplete());
+ } else if (optional('if', next)) {
+ return new Nested(ifCondition, const IfComplete());
+ } else if (optional('...', next) || optional('...?', next)) {
return const IfSpread();
}
- // TODO(danrubel): nested control flow structures
return const IfEntry();
}
}
@@ -158,12 +172,7 @@
const IfSpread();
@override
- LiteralEntryInfo computeNext(Token token) {
- if (optional('else', token.next)) {
- return const IfElse();
- }
- return const IfComplete();
- }
+ LiteralEntryInfo computeNext(Token token) => const IfComplete();
}
/// A step for parsing a literal list, set, or map entry
@@ -172,11 +181,23 @@
const IfEntry() : super(true);
@override
- LiteralEntryInfo computeNext(Token token) {
- if (optional('else', token.next)) {
- return const IfElse();
+ LiteralEntryInfo computeNext(Token token) => const IfComplete();
+}
+
+class IfComplete extends LiteralEntryInfo {
+ const IfComplete() : super(false);
+
+ @override
+ Token parse(Token token, Parser parser) {
+ if (!optional('else', token.next)) {
+ parser.listener.endIfControlFlow(token);
}
- return const IfComplete();
+ return token;
+ }
+
+ @override
+ LiteralEntryInfo computeNext(Token token) {
+ return optional('else', token.next) ? const IfElse() : null;
}
}
@@ -196,10 +217,14 @@
LiteralEntryInfo computeNext(Token token) {
assert(optional('else', token));
Token next = token.next;
- if (optional('...', next) || optional('...?', next)) {
+ if (optional('for', next) ||
+ (optional('await', next) && optional('for', next.next))) {
+ return new Nested(new ForCondition(), const IfElseComplete());
+ } else if (optional('if', next)) {
+ return new Nested(ifCondition, const IfElseComplete());
+ } else if (optional('...', next) || optional('...?', next)) {
return const ElseSpread();
}
- // TODO(danrubel): nested control flow structures
return const ElseEntry();
}
}
@@ -222,16 +247,6 @@
}
}
-class IfComplete extends LiteralEntryInfo {
- const IfComplete() : super(false);
-
- @override
- Token parse(Token token, Parser parser) {
- parser.listener.endIfControlFlow(token);
- return token;
- }
-}
-
class IfElseComplete extends LiteralEntryInfo {
const IfElseComplete() : super(false);
@@ -255,3 +270,22 @@
return token;
}
}
+
+class Nested extends LiteralEntryInfo {
+ LiteralEntryInfo nestedStep;
+ final LiteralEntryInfo lastStep;
+
+ Nested(this.nestedStep, this.lastStep) : super(false);
+
+ @override
+ bool get hasEntry => nestedStep.hasEntry;
+
+ @override
+ Token parse(Token token, Parser parser) => nestedStep.parse(token, parser);
+
+ @override
+ LiteralEntryInfo computeNext(Token token) {
+ nestedStep = nestedStep.computeNext(token);
+ return nestedStep != null ? this : lastStep;
+ }
+}
diff --git a/pkg/front_end/test/fasta/parser/literal_entry_info_test.dart b/pkg/front_end/test/fasta/parser/literal_entry_info_test.dart
index 9e4620f..0da8a6c 100644
--- a/pkg/front_end/test/fasta/parser/literal_entry_info_test.dart
+++ b/pkg/front_end/test/fasta/parser/literal_entry_info_test.dart
@@ -96,6 +96,143 @@
);
}
+ test_forForFor() {
+ parseEntry(
+ 'before for (var i = 0; i < 10; ++i) for (x in y) for (var a in [6]) 2',
+ [
+ // for (var i = 0; i < 10; ++i)
+ 'beginForControlFlow null for',
+ 'beginMetadataStar var',
+ 'endMetadataStar 0',
+ 'handleNoTypeArguments var',
+ 'beginVariablesDeclaration i var',
+ 'handleIdentifier i localVariableDeclaration',
+ 'beginInitializedIdentifier i',
+ 'beginVariableInitializer =',
+ 'handleLiteralInt 0',
+ 'endVariableInitializer =',
+ 'endInitializedIdentifier i',
+ 'endVariablesDeclaration 1 null',
+ 'handleForInitializerLocalVariableDeclaration 0',
+ 'handleIdentifier i expression',
+ 'handleNoTypeArguments <',
+ 'handleNoArguments <',
+ 'handleSend i <',
+ 'beginBinaryExpression <',
+ 'handleLiteralInt 10',
+ 'endBinaryExpression <',
+ 'handleExpressionStatement ;',
+ 'handleIdentifier i expression',
+ 'handleNoTypeArguments )',
+ 'handleNoArguments )',
+ 'handleSend i )',
+ 'handleUnaryPrefixAssignmentExpression ++',
+ 'handleForInitializerExpressionStatement for ( ; 1',
+ // nested for (x in y)
+ 'beginForControlFlow null for',
+ 'handleIdentifier x expression',
+ 'handleNoTypeArguments in',
+ 'handleNoArguments in',
+ 'handleSend x in',
+ 'handleForInitializerExpressionStatement x',
+ 'beginForInExpression y',
+ 'handleIdentifier y expression',
+ 'handleNoTypeArguments )',
+ 'handleNoArguments )',
+ 'handleSend y )',
+ 'endForInExpression )',
+ 'handleForInLoopParts null for ( in',
+ // nested for (var a in [6])
+ 'beginForControlFlow null for',
+ 'beginMetadataStar var',
+ 'endMetadataStar 0',
+ 'handleNoTypeArguments var',
+ 'beginVariablesDeclaration a var',
+ 'handleIdentifier a localVariableDeclaration',
+ 'beginInitializedIdentifier a',
+ 'handleNoVariableInitializer in',
+ 'endInitializedIdentifier a',
+ 'endVariablesDeclaration 1 null',
+ 'handleForInitializerLocalVariableDeclaration a',
+ 'beginForInExpression [',
+ 'handleNoTypeArguments [',
+ 'handleLiteralInt 6',
+ 'handleLiteralList 1, [, null, ]',
+ 'endForInExpression )',
+ 'handleForInLoopParts null for ( in',
+ // entry
+ 'handleLiteralInt 2',
+ // end nested for
+ 'endForInControlFlow 2',
+ // end nested for
+ 'endForInControlFlow 2',
+ // end for
+ 'endForControlFlow 2',
+ ],
+ );
+ }
+
+ test_forIfForElse() {
+ parseEntry(
+ 'before await for (var x in y) if (a) for (b in c) 2 else 7',
+ [
+ // await for (var x in y)
+ 'beginForControlFlow await for',
+ 'beginMetadataStar var',
+ 'endMetadataStar 0',
+ 'handleNoTypeArguments var',
+ 'beginVariablesDeclaration x var',
+ 'handleIdentifier x localVariableDeclaration',
+ 'beginInitializedIdentifier x',
+ 'handleNoVariableInitializer in',
+ 'endInitializedIdentifier x',
+ 'endVariablesDeclaration 1 null',
+ 'handleForInitializerLocalVariableDeclaration x',
+ 'beginForInExpression y',
+ 'handleIdentifier y expression',
+ 'handleNoTypeArguments )',
+ 'handleNoArguments )',
+ 'handleSend y )',
+ 'endForInExpression )',
+ 'handleForInLoopParts await for ( in',
+ // nested if (a)
+ 'beginIfControlFlow if',
+ 'handleIdentifier a expression',
+ 'handleNoTypeArguments )',
+ 'handleNoArguments )',
+ 'handleSend a )',
+ 'handleParenthesizedCondition (',
+ // nested for (b in c)
+ 'beginForControlFlow null for',
+ 'handleIdentifier b expression',
+ 'handleNoTypeArguments in',
+ 'handleNoArguments in',
+ 'handleSend b in',
+ 'handleForInitializerExpressionStatement b',
+ 'beginForInExpression c',
+ 'handleIdentifier c expression',
+ 'handleNoTypeArguments )',
+ 'handleNoArguments )',
+ 'handleSend c )',
+ 'endForInExpression )',
+ 'handleForInLoopParts null for ( in',
+ // for-if-for-entry
+ 'handleLiteralInt 2',
+ // end nested for
+ 'endForInControlFlow 2',
+ // nested else
+ 'handleElseControlFlow else',
+ // nested if-else-entry
+ 'handleLiteralInt 7',
+ // end nested else
+ 'endIfElseControlFlow 7',
+ // end for
+ 'endForInControlFlow 7',
+ ],
+ inAsync: true,
+ );
+ }
+
test_forIn() {
parseEntry(
'before await for (var x in y) 2',
@@ -219,6 +356,99 @@
);
}
+ test_ifFor() {
+ parseEntry(
+ 'before if (true) for (x in y) 2',
+ [
+ // if (true)
+ 'beginIfControlFlow if',
+ 'handleLiteralBool true',
+ 'handleParenthesizedCondition (',
+ // nested for (x in y)
+ 'beginForControlFlow null for',
+ 'handleIdentifier x expression',
+ 'handleNoTypeArguments in',
+ 'handleNoArguments in',
+ 'handleSend x in',
+ 'handleForInitializerExpressionStatement x',
+ 'beginForInExpression y',
+ 'handleIdentifier y expression',
+ 'handleNoTypeArguments )',
+ 'handleNoArguments )',
+ 'handleSend y )',
+ 'endForInExpression )',
+ 'handleForInLoopParts null for ( in',
+ // if-for-entry
+ 'handleLiteralInt 2',
+ // end nested for
+ 'endForInControlFlow 2',
+ // end if
+ 'endIfControlFlow 2',
+ ],
+ );
+ }
+
+ test_ifForElseIfFor() {
+ parseEntry(
+ 'before if (true) for (a in b) 2 else if (c) for (d in e) 5',
+ [
+ // if (true)
+ 'beginIfControlFlow if',
+ 'handleLiteralBool true',
+ 'handleParenthesizedCondition (',
+ // nested for (a in b)
+ 'beginForControlFlow null for',
+ 'handleIdentifier a expression',
+ 'handleNoTypeArguments in',
+ 'handleNoArguments in',
+ 'handleSend a in',
+ 'handleForInitializerExpressionStatement a',
+ 'beginForInExpression b',
+ 'handleIdentifier b expression',
+ 'handleNoTypeArguments )',
+ 'handleNoArguments )',
+ 'handleSend b )',
+ 'endForInExpression )',
+ 'handleForInLoopParts null for ( in',
+ // if-for-entry
+ 'handleLiteralInt 2',
+ // end nested for
+ 'endForInControlFlow 2',
+ // else
+ 'handleElseControlFlow else',
+ // nested if (c)
+ 'beginIfControlFlow if',
+ 'handleIdentifier c expression',
+ 'handleNoTypeArguments )',
+ 'handleNoArguments )',
+ 'handleSend c )',
+ 'handleParenthesizedCondition (',
+ // nested for (d in e)
+ 'beginForControlFlow null for',
+ 'handleIdentifier d expression',
+ 'handleNoTypeArguments in',
+ 'handleNoArguments in',
+ 'handleSend d in',
+ 'handleForInitializerExpressionStatement d',
+ 'beginForInExpression e',
+ 'handleIdentifier e expression',
+ 'handleNoTypeArguments )',
+ 'handleNoArguments )',
+ 'handleSend e )',
+ 'endForInExpression )',
+ 'handleForInLoopParts null for ( in',
+ // else-if-for-entry
+ 'handleLiteralInt 5',
+ // end nested for
+ 'endForInControlFlow 5',
+ // end nested if
+ 'endIfControlFlow 5',
+ // end else
+ 'endIfElseControlFlow 5',
+ ],
+ );
+ }
+
test_ifSpreadQ() {
parseEntry(
'before if (true) ...?[2]',