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]',