Parse simple for control flow collection entires

... and address comments in:
https://dart-review.googlesource.com/c/sdk/+/91144
https://dart-review.googlesource.com/c/sdk/+/90901

Change-Id: I7a9288bf28e069832193bd8a196a11f9a407b20d
Reviewed-on: https://dart-review.googlesource.com/c/91300
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analyzer/lib/dart/ast/ast_factory.dart b/pkg/analyzer/lib/dart/ast/ast_factory.dart
index 3c4473f..d7049da 100644
--- a/pkg/analyzer/lib/dart/ast/ast_factory.dart
+++ b/pkg/analyzer/lib/dart/ast/ast_factory.dart
@@ -189,7 +189,8 @@
    * literal.
    */
   CollectionForElement collectionForElement(
-      {Token forKeyword,
+      {Token awaitKeyword,
+      Token forKeyword,
       Token leftParenthesis,
       ForLoopParts forLoopParts,
       Token rightParenthesis,
@@ -845,7 +846,8 @@
    * Returns a newly created for element that can be part of a map literal.
    */
   MapForElement mapForElement(
-      {Token forKeyword,
+      {Token awaitKeyword,
+      Token forKeyword,
       Token leftParenthesis,
       ForLoopParts forLoopParts,
       Token rightParenthesis,
diff --git a/pkg/analyzer/lib/src/fasta/ast_builder.dart b/pkg/analyzer/lib/src/fasta/ast_builder.dart
index 79bdc7e..e5a4e59 100644
--- a/pkg/analyzer/lib/src/fasta/ast_builder.dart
+++ b/pkg/analyzer/lib/src/fasta/ast_builder.dart
@@ -308,7 +308,7 @@
   void pushIfControlFlowInfo(Token ifToken, ParenthesizedExpression condition,
       var thenElement, Token elseToken, var elseElement) {
     if (enableControlFlowCollections) {
-      push(new _ControlFlowInfo(
+      push(new _IfControlFlowInfo(
           ifToken,
           condition.leftParenthesis,
           condition.expression,
@@ -879,6 +879,66 @@
   }
 
   @override
+  void endForControlFlow(Token rightParenthesis) {
+    debugEvent("endForControlFlow");
+    var entry = pop();
+
+    int updateExpressionCount = pop();
+    Token leftSeparator = pop();
+    Token leftParenthesis = pop();
+    Token forToken = pop();
+
+    List<Expression> updates = popTypedList(updateExpressionCount);
+    Statement conditionStatement = pop();
+    Object initializerPart = pop();
+
+    Expression condition;
+    Token rightSeparator;
+    if (conditionStatement is ExpressionStatement) {
+      condition = conditionStatement.expression;
+      rightSeparator = conditionStatement.semicolon;
+    } else {
+      rightSeparator = (conditionStatement as EmptyStatement).semicolon;
+    }
+
+    ForLoopParts forLoopParts;
+    Expression initializer;
+    if (initializerPart is VariableDeclarationStatement) {
+      forLoopParts = ast.forPartsWithDeclarations(
+        variables: initializerPart.variables,
+        leftSeparator: leftSeparator,
+        condition: condition,
+        rightSeparator: rightSeparator,
+        updaters: updates,
+      );
+    } else {
+      initializer = initializerPart as Expression;
+      forLoopParts = ast.forPartsWithExpression(
+        initialization: initializer,
+        leftSeparator: leftSeparator,
+        condition: condition,
+        rightSeparator: rightSeparator,
+        updaters: updates,
+      );
+    }
+
+    pushForControlFlowInfo(
+        null, forToken, leftParenthesis, forLoopParts, entry);
+  }
+
+  void pushForControlFlowInfo(Token awaitToken, Token forToken,
+      Token leftParenthesis, ForLoopParts forLoopParts, Object entry) {
+    if (enableControlFlowCollections) {
+      push(new _ForControlFlowInfo(awaitToken, forToken, leftParenthesis,
+          forLoopParts, leftParenthesis.endGroup, entry));
+    } else {
+      handleRecoverableError(
+          templateUnexpectedToken.withArguments(forToken), forToken, forToken);
+      push(entry);
+    }
+  }
+
+  @override
   void endForStatement(Token endToken) {
     debugEvent("ForStatement");
     Statement body = pop();
@@ -1305,6 +1365,54 @@
   }
 
   @override
+  void endForInControlFlow(Token rightParenthesis) {
+    debugEvent("endForInControlFlow");
+    var entry = pop();
+
+    Token inKeyword = pop();
+    Token leftParenthesis = pop();
+    Token forToken = pop();
+    Token awaitToken = pop(NullValue.AwaitToken);
+
+    Expression iterator = pop();
+    Object variableOrDeclaration = pop();
+
+    ForLoopParts forLoopParts;
+    if (variableOrDeclaration is VariableDeclarationStatement) {
+      VariableDeclarationList variableList = variableOrDeclaration.variables;
+      forLoopParts = ast.forEachPartsWithDeclaration(
+        loopVariable: ast.declaredIdentifier(
+            variableList.documentationComment,
+            variableList.metadata,
+            variableList.keyword,
+            variableList.type,
+            variableList.variables.first.name),
+        inKeyword: inKeyword,
+        iterable: iterator,
+      );
+    } else {
+      if (variableOrDeclaration is! SimpleIdentifier) {
+        // Parser has already reported the error.
+        if (!leftParenthesis.next.isIdentifier) {
+          parser.rewriter.insertToken(
+              leftParenthesis,
+              new SyntheticStringToken(
+                  TokenType.IDENTIFIER, '', leftParenthesis.next.charOffset));
+        }
+        variableOrDeclaration = ast.simpleIdentifier(leftParenthesis.next);
+      }
+      forLoopParts = ast.forEachPartsWithIdentifier(
+        identifier: variableOrDeclaration,
+        inKeyword: inKeyword,
+        iterable: iterator,
+      );
+    }
+
+    pushForControlFlowInfo(
+        awaitToken, forToken, leftParenthesis, forLoopParts, entry);
+  }
+
+  @override
   void endForIn(Token endToken) {
     debugEvent("ForInExpression");
 
@@ -3190,7 +3298,43 @@
   MapElement asMapElement(AstFactory ast);
 }
 
-class _ControlFlowInfo implements _EntryInfo {
+class _ForControlFlowInfo implements _EntryInfo {
+  final Token awaitToken;
+  final Token forKeyword;
+  final Token leftParenthesis;
+  final ForLoopParts forLoopParts;
+  final Token rightParenthesis;
+  final entry;
+
+  _ForControlFlowInfo(this.awaitToken, this.forKeyword, this.leftParenthesis,
+      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,
+    );
+  }
+
+  @override
+  MapElement asMapElement(AstFactory ast) {
+    return ast.mapForElement(
+      awaitKeyword: awaitToken,
+      forKeyword: forKeyword,
+      leftParenthesis: leftParenthesis,
+      forLoopParts: forLoopParts,
+      rightParenthesis: rightParenthesis,
+      body: entry,
+    );
+  }
+}
+
+class _IfControlFlowInfo implements _EntryInfo {
   final Token ifToken;
   final Token leftParenthesis;
   final Expression conditionExpression;
@@ -3199,7 +3343,7 @@
   final Token elseToken;
   final elseElement;
 
-  _ControlFlowInfo(
+  _IfControlFlowInfo(
       this.ifToken,
       this.leftParenthesis,
       this.conditionExpression,
diff --git a/pkg/analyzer/test/generated/parser_fasta_listener.dart b/pkg/analyzer/test/generated/parser_fasta_listener.dart
index 8fea9c5..89b0d38 100644
--- a/pkg/analyzer/test/generated/parser_fasta_listener.dart
+++ b/pkg/analyzer/test/generated/parser_fasta_listener.dart
@@ -214,6 +214,12 @@
   }
 
   @override
+  void beginForControlFlow(Token awaitToken, Token forToken) {
+    super.beginForControlFlow(awaitToken, forToken);
+    begin('ForControlFlow');
+  }
+
+  @override
   void beginForInBody(Token token) {
     super.beginForInBody(token);
     begin('ForInBody');
@@ -704,6 +710,18 @@
   }
 
   @override
+  void endForControlFlow(Token rightParenthesis) {
+    end('ForControlFlow');
+    super.endForControlFlow(rightParenthesis);
+  }
+
+  @override
+  void endForInControlFlow(Token rightParenthesis) {
+    end('ForControlFlow');
+    super.endForInControlFlow(rightParenthesis);
+  }
+
+  @override
   void endForIn(Token endToken) {
     end('ForStatement');
     super.endForIn(endToken);
diff --git a/pkg/analyzer/test/generated/parser_fasta_test.dart b/pkg/analyzer/test/generated/parser_fasta_test.dart
index 1c07949..8824589 100644
--- a/pkg/analyzer/test/generated/parser_fasta_test.dart
+++ b/pkg/analyzer/test/generated/parser_fasta_test.dart
@@ -15,6 +15,7 @@
 import 'package:analyzer/src/generated/parser.dart' as analyzer;
 import 'package:analyzer/src/generated/utilities_dart.dart';
 import 'package:analyzer/src/string_source.dart';
+import 'package:front_end/src/fasta/parser/async_modifier.dart';
 import 'package:front_end/src/fasta/parser/forwarding_listener.dart' as fasta;
 import 'package:front_end/src/fasta/parser/parser.dart' as fasta;
 import 'package:front_end/src/fasta/scanner.dart'
@@ -113,31 +114,61 @@
   Expression parseCollectionLiteral(String source,
       {List<ErrorCode> codes,
       List<ExpectedError> errors,
-      int expectedEndOffset}) {
+      int expectedEndOffset,
+      bool inAsync = false}) {
     return parseExpression(source,
         codes: codes,
         errors: errors,
         expectedEndOffset: expectedEndOffset,
+        inAsync: inAsync,
         parseSetLiterals: true,
         parseSpreadCollections: true,
         parseControlFlowCollections: true);
   }
 
-  @failingTest
   void test_listLiteral_for() {
-    ListLiteral2 list = parseCollectionLiteral('[1, for (var x in list) 2]');
+    ListLiteral2 list = parseCollectionLiteral(
+      '[1, await for (var x in list) 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');
   }
 
-  @failingTest
   void test_listLiteral_forSpread() {
     ListLiteral2 list =
-        parseCollectionLiteral('[1, for (var x in list) ...[2]]');
+        parseCollectionLiteral('[1, for (int x = 0; x < 10; ++x) ...[2]]');
     expect(list.elements, hasLength(2));
     IntegerLiteral first = list.elements[0];
     expect(first.value, 1);
+
+    CollectionForElement second = list.elements[1];
+    expect(second.awaitKeyword, isNull);
+    expect(second.forKeyword.isKeyword, isTrue);
+    expect(second.leftParenthesis.lexeme, '(');
+    expect(second.rightParenthesis.lexeme, ')');
+    ForPartsWithDeclarations forLoopParts = second.forLoopParts;
+    VariableDeclaration forLoopVar = forLoopParts.variables.variables[0];
+    expect(forLoopVar.name.name, 'x');
+    BinaryExpression condition = forLoopParts.condition;
+    IntegerLiteral rightOperand = condition.rightOperand;
+    expect(rightOperand.value, 10);
+    PrefixExpression updater = forLoopParts.updaters[0];
+    SimpleIdentifier updaterOperand = updater.operand;
+    expect(updaterOperand.name, 'x');
   }
 
   void test_listLiteral_if() {
@@ -223,6 +254,52 @@
     expect(spreadExpression.elements, hasLength(1));
   }
 
+  void test_mapLiteral_for() {
+    MapLiteral2 map = parseCollectionLiteral('{1:7, await for (y in list) 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');
+  }
+
+  void test_mapLiteral_forSpread() {
+    MapLiteral2 map =
+        parseCollectionLiteral('{1:7, for (x = 0; x < 10; ++x) ...{2:3}}');
+    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, isNull);
+    expect(second.forKeyword.isKeyword, isTrue);
+    expect(second.leftParenthesis.lexeme, '(');
+    expect(second.rightParenthesis.lexeme, ')');
+    ForPartsWithExpression forLoopParts = second.forLoopParts;
+    AssignmentExpression forLoopInit = forLoopParts.initialization;
+    SimpleIdentifier forLoopVar = forLoopInit.leftHandSide;
+    expect(forLoopVar.name, 'x');
+    BinaryExpression condition = forLoopParts.condition;
+    IntegerLiteral rightOperand = condition.rightOperand;
+    expect(rightOperand.value, 10);
+    PrefixExpression updater = forLoopParts.updaters[0];
+    SimpleIdentifier updaterOperand = updater.operand;
+    expect(updaterOperand.name, 'x');
+  }
+
   void test_mapLiteral_if() {
     MapLiteral2 map = parseCollectionLiteral('{1:1, if (true) 2:4}');
     expect(map.entries, hasLength(2));
@@ -1212,6 +1289,7 @@
       {List<ErrorCode> codes,
       List<ExpectedError> errors,
       int expectedEndOffset,
+      bool inAsync = false,
       bool parseSetLiterals = false,
       bool parseSpreadCollections = false,
       bool parseControlFlowCollections = false}) {
@@ -1220,6 +1298,9 @@
     _parserProxy.astBuilder.enableSpreadCollections = parseSpreadCollections;
     _parserProxy.astBuilder.enableControlFlowCollections =
         parseControlFlowCollections;
+    if (inAsync) {
+      _parserProxy.fastaParser.asyncState = AsyncModifier.Async;
+    }
     Expression result = _parserProxy.parseExpression2();
     assertErrors(codes: codes, errors: errors);
     return result;
diff --git a/pkg/analyzer/test/src/fasta/recovery/partial_code/forEach_statement_test.dart b/pkg/analyzer/test/src/fasta/recovery/partial_code/forEach_statement_test.dart
index 0f15628e5..2d7ef0f 100644
--- a/pkg/analyzer/test/src/fasta/recovery/partial_code/forEach_statement_test.dart
+++ b/pkg/analyzer/test/src/fasta/recovery/partial_code/forEach_statement_test.dart
@@ -63,6 +63,7 @@
                 ParserErrorCode.MISSING_IDENTIFIER,
                 ParserErrorCode.MISSING_IDENTIFIER,
                 ParserErrorCode.MISSING_IDENTIFIER,
+                // TODO(danrubel): investigate why 4 missing identifier errors
                 ParserErrorCode.MISSING_IDENTIFIER,
                 ParserErrorCode.EXPECTED_TOKEN,
                 ParserErrorCode.EXPECTED_TOKEN
diff --git a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
index 49b3d21..7709f11 100644
--- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
@@ -2250,6 +2250,29 @@
   }
 
   @override
+  void endForControlFlow(Token rightParenthesis) {
+    debugEvent("endForControlFlow");
+    // TODO(danrubel) implement control flow support
+    var entry = pop();
+
+    int updateExpressionCount = pop();
+    pop(); // left separator
+    pop(); // left parenthesis
+    Token forToken = pop();
+
+    popListForEffect(updateExpressionCount); // updates
+    popStatement(); // condition
+    Object variableOrExpression = pop();
+    buildVariableDeclarations(variableOrExpression); // variables
+
+    push(entry); // push the entry back on the stack and drop the rest
+    handleRecoverableError(
+        fasta.templateUnexpectedToken.withArguments(forToken),
+        forToken,
+        forToken);
+  }
+
+  @override
   void endForStatement(Token endToken) {
     debugEvent("ForStatement");
     Statement body = popStatement();
@@ -3531,7 +3554,7 @@
 
   @override
   void endIfControlFlow(Token token) {
-    debugEvent("IfControlFlow");
+    debugEvent("endIfControlFlow");
     // TODO(danrubel) implement control flow support
     var entry = pop();
     pop(); // parenthesized expression
@@ -3543,7 +3566,7 @@
 
   @override
   void endIfElseControlFlow(Token token) {
-    debugEvent("IfElseControlFlow");
+    debugEvent("endIfElseControlFlow");
     // TODO(danrubel) implement control flow support
     pop(); // else entry
     var entry = pop(); // then entry
@@ -3851,6 +3874,26 @@
   }
 
   @override
+  void endForInControlFlow(Token rightParenthesis) {
+    debugEvent("endForInControlFlow");
+    // TODO(danrubel) implement control flow support
+    var entry = pop();
+
+    pop(); // `in` keyword
+    Token forToken = pop();
+    pop(NullValue.AwaitToken); // await token
+
+    popForValue(); // expression
+    pop(); // lvalue
+
+    push(entry); // push the entry back on the stack and drop the rest
+    handleRecoverableError(
+        fasta.templateUnexpectedToken.withArguments(forToken),
+        forToken,
+        forToken);
+  }
+
+  @override
   void endForIn(Token endToken) {
     debugEvent("ForIn");
     Statement body = popStatement();
diff --git a/pkg/front_end/lib/src/fasta/parser/forwarding_listener.dart b/pkg/front_end/lib/src/fasta/parser/forwarding_listener.dart
index decbb5a..241a22e 100644
--- a/pkg/front_end/lib/src/fasta/parser/forwarding_listener.dart
+++ b/pkg/front_end/lib/src/fasta/parser/forwarding_listener.dart
@@ -147,6 +147,11 @@
   }
 
   @override
+  void beginForControlFlow(Token awaitToken, Token forToken) {
+    listener?.beginForControlFlow(awaitToken, forToken);
+  }
+
+  @override
   void beginForInBody(Token token) {
     listener?.beginForInBody(token);
   }
@@ -564,6 +569,16 @@
   }
 
   @override
+  void endForControlFlow(Token rightParenthesis) {
+    listener?.endForControlFlow(rightParenthesis);
+  }
+
+  @override
+  void endForInControlFlow(Token rightParenthesis) {
+    listener?.endForInControlFlow(rightParenthesis);
+  }
+
+  @override
   void endForIn(Token endToken) {
     listener?.endForIn(endToken);
   }
diff --git a/pkg/front_end/lib/src/fasta/parser/listener.dart b/pkg/front_end/lib/src/fasta/parser/listener.dart
index 7c6fb85..b460e8b 100644
--- a/pkg/front_end/lib/src/fasta/parser/listener.dart
+++ b/pkg/front_end/lib/src/fasta/parser/listener.dart
@@ -1084,6 +1084,19 @@
     logEvent("ConstExpression");
   }
 
+  /// Called before parsing a "for" control flow list, set, or map entry.
+  void beginForControlFlow(Token awaitToken, Token forToken) {}
+
+  /// Called after parsing a "for" control flow list, set, or map entry.
+  void endForControlFlow(Token rightParenthesis) {
+    logEvent('endForControlFlow');
+  }
+
+  /// Called after parsing a "for-in" control flow list, set, or map entry.
+  void endForInControlFlow(Token rightParenthesis) {
+    logEvent('endForInControlFlow');
+  }
+
   /// Called before parsing an `if` control flow list, set, or map entry.
   void beginIfControlFlow(Token ifToken) {}
 
@@ -1096,7 +1109,7 @@
   /// - if conditional expression
   /// - expression
   void endIfControlFlow(Token token) {
-    logEvent("IfControlFlow");
+    logEvent("endIfControlFlow");
   }
 
   /// Called after parsing an if-else control flow list, set, or map entry.
@@ -1105,7 +1118,7 @@
   /// - then expression
   /// - else expression
   void endIfElseControlFlow(Token token) {
-    logEvent("IfElseControlFlow");
+    logEvent("endIfElseControlFlow");
   }
 
   /// Called after parsing a list, set, or map entry that starts with
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 dc7d5c2..1af5aeb 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
@@ -50,7 +50,9 @@
   if (optional('if', next)) {
     return ifCondition;
   } else if (optional('for', next)) {
-    return forCondition;
+    return new ForCondition();
+  } else if (optional('await', next) && optional('for', next.next)) {
+    return new ForCondition();
   } else if (optional('...', next) || optional('...?', next)) {
     return spreadOperator;
   }
@@ -59,9 +61,10 @@
 
 /// Return `true` if the given [token] should be treated like the start of
 /// a literal entry in a list, set, or map for the purposes of recovery.
-bool looksLikeLiteralEntry(Token next) =>
-    looksLikeExpressionStart(next) ||
-    optional('...', next) ||
-    optional('...?', next) ||
-    optional('if', next) ||
-    optional('for', next);
+bool looksLikeLiteralEntry(Token token) =>
+    looksLikeExpressionStart(token) ||
+    optional('...', token) ||
+    optional('...?', token) ||
+    optional('if', token) ||
+    optional('for', token) ||
+    (optional('await', token) && optional('for', token.next));
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 5f200e9..1c0667c 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
@@ -3,16 +3,10 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import '../../scanner/token.dart';
-import '../fasta_codes.dart' show templateUnexpectedToken;
-import 'identifier_context.dart';
 import 'literal_entry_info.dart';
 import 'parser.dart';
 import 'util.dart';
 
-/// [forCondition] is the first step for parsing a literal entry
-/// starting with the `for` control flow.
-const LiteralEntryInfo forCondition = const ForCondition();
-
 /// [ifCondition] is the first step for parsing a literal entry
 /// starting with `if` control flow.
 const LiteralEntryInfo ifCondition = const IfCondition();
@@ -27,16 +21,111 @@
 
 /// The first step when processing a `for` control flow collection entry.
 class ForCondition extends LiteralEntryInfo {
-  const ForCondition() : super(false);
+  bool inStyle;
+
+  ForCondition() : super(false);
 
   @override
   Token parse(Token token, Parser parser) {
-    final forToken = token.next;
+    Token next = token.next;
+    Token awaitToken;
+    if (optional('await', next)) {
+      awaitToken = token = next;
+      next = token.next;
+    }
+    final forToken = next;
     assert(optional('for', forToken));
-    // TODO(danrubel): implement `for` control flow collection entries
-    parser.reportRecoverableErrorWithToken(forToken, templateUnexpectedToken);
-    parser.ensureIdentifier(forToken, IdentifierContext.expression);
-    return forToken;
+    parser.listener.beginForControlFlow(awaitToken, forToken);
+
+    token = parser.parseForLoopPartsStart(awaitToken, forToken);
+    Token identifier = token.next;
+    token = parser.parseForLoopPartsMid(token, awaitToken, forToken);
+
+    if (optional('in', token.next) || optional(':', token.next)) {
+      // Process `for ( ... in ... )`
+      inStyle = true;
+      token = parser.parseForInLoopPartsRest(
+          token, awaitToken, forToken, identifier);
+    } else {
+      // Process `for ( ... ; ... ; ... )`
+      inStyle = false;
+      token = parser.parseForLoopPartsRest(token, forToken, awaitToken);
+    }
+    return token;
+  }
+
+  @override
+  LiteralEntryInfo computeNext(Token token) {
+    Token next = token.next;
+    if (optional('...', next) || optional('...?', next)) {
+      return inStyle ? const ForInSpread() : const ForSpread();
+    }
+    // TODO(danrubel): nested control flow structures
+    return inStyle ? const ForInEntry() : const ForEntry();
+  }
+}
+
+/// A step for parsing a spread collection
+/// as the "for" control flow's expression.
+class ForSpread extends SpreadOperator {
+  const ForSpread();
+
+  @override
+  LiteralEntryInfo computeNext(Token token) {
+    return const ForComplete();
+  }
+}
+
+/// A step for parsing a spread collection
+/// as the "for-in" control flow's expression.
+class ForInSpread extends SpreadOperator {
+  const ForInSpread();
+
+  @override
+  LiteralEntryInfo computeNext(Token token) {
+    return const ForInComplete();
+  }
+}
+
+/// A step for parsing a literal list, set, or map entry
+/// as the "for" control flow's expression.
+class ForEntry extends LiteralEntryInfo {
+  const ForEntry() : super(true);
+
+  @override
+  LiteralEntryInfo computeNext(Token token) {
+    return const ForComplete();
+  }
+}
+
+/// A step for parsing a literal list, set, or map entry
+/// as the "for-in" control flow's expression.
+class ForInEntry extends LiteralEntryInfo {
+  const ForInEntry() : super(true);
+
+  @override
+  LiteralEntryInfo computeNext(Token token) {
+    return const ForInComplete();
+  }
+}
+
+class ForComplete extends LiteralEntryInfo {
+  const ForComplete() : super(false);
+
+  @override
+  Token parse(Token token, Parser parser) {
+    parser.listener.endForControlFlow(token);
+    return token;
+  }
+}
+
+class ForInComplete extends LiteralEntryInfo {
+  const ForInComplete() : super(false);
+
+  @override
+  Token parse(Token token, Parser parser) {
+    parser.listener.endForInControlFlow(token);
+    return token;
   }
 }
 
diff --git a/pkg/front_end/lib/src/fasta/parser/parser.dart b/pkg/front_end/lib/src/fasta/parser/parser.dart
index cd79c96..0baa173 100644
--- a/pkg/front_end/lib/src/fasta/parser/parser.dart
+++ b/pkg/front_end/lib/src/fasta/parser/parser.dart
@@ -5316,8 +5316,8 @@
 
     token = parseForLoopPartsStart(awaitToken, forToken);
     Token identifier = token.next;
-    token = parseForLoopPartsMid(token, forToken);
-    if (looksLikeForInLoopParts(awaitToken, token.next)) {
+    token = parseForLoopPartsMid(token, awaitToken, forToken);
+    if (optional('in', token.next) || optional(':', token.next)) {
       // Process `for ( ... in ... )`
       return parseForInRest(token, awaitToken, forToken, identifier);
     } else {
@@ -5375,7 +5375,7 @@
 
   /// Parse the remainder of the local variable declaration
   /// or an expression if no local variable declaration was found.
-  Token parseForLoopPartsMid(Token token, Token forToken) {
+  Token parseForLoopPartsMid(Token token, Token awaitToken, Token forToken) {
     if (token != forToken.next) {
       token = parseVariablesDeclarationRest(token, false);
       listener.handleForInitializerLocalVariableDeclaration(token);
@@ -5385,29 +5385,38 @@
       token = parseExpression(token);
       listener.handleForInitializerExpressionStatement(token);
     }
+    Token next = token.next;
+    if (optional(';', next)) {
+      if (awaitToken != null) {
+        reportRecoverableError(awaitToken, fasta.messageInvalidAwaitFor);
+      }
+    } else if (!optional('in', next)) {
+      // Recovery
+      if (optional(':', next)) {
+        reportRecoverableError(next, fasta.messageColonInPlaceOfIn);
+      } else if (awaitToken != null) {
+        reportRecoverableError(
+            next, fasta.templateExpectedButGot.withArguments('in'));
+        token.setNext(
+            new SyntheticKeywordToken(Keyword.IN, next.offset)..setNext(next));
+      }
+    }
     return token;
   }
 
-  /// Return true if the combination of the [awaitToken] and [inKeyword]
-  /// indicate that a for loop is being parsed of the form:
-  /// (`await`)? `for` `(` type? identifier `in`
-  bool looksLikeForInLoopParts(Token awaitToken, Token inKeyword) =>
-      optional('in', inKeyword) ||
-      optional(':', inKeyword) ||
-      (!optional(';', inKeyword) && awaitToken != null);
-
   /// This method parses the portion of the forLoopParts that starts with the
   /// first semicolon (the one that terminates the forInitializerStatement).
   ///
   /// ```
-  /// forLoopParts:
-  ///   forInitializerStatement expression? ';' expressionList? |
-  ///   declaredIdentifier 'in' expression |
-  ///   identifier 'in' expression
+  ///  forLoopParts:
+  ///      localVariableDeclaration ';' expression? ';' expressionList?
+  ///    | expression? ';' expression? ';' expressionList?
+  ///    | localVariableDeclaration 'in' expression
+  ///    | identifier 'in' expression
   /// ;
   /// ```
   Token parseForRest(Token awaitToken, Token token, Token forToken) {
-    token = parseForLoopPartsRest(forToken, awaitToken, token);
+    token = parseForLoopPartsRest(token, forToken, awaitToken);
     listener.beginForStatementBody(token.next);
     LoopState savedLoopState = loopState;
     loopState = LoopState.InsideLoop;
@@ -5418,14 +5427,11 @@
     return token;
   }
 
-  Token parseForLoopPartsRest(Token forToken, Token awaitToken, Token token) {
+  Token parseForLoopPartsRest(Token token, Token forToken, Token awaitToken) {
     Token leftParenthesis = forToken.next;
     assert(optional('for', forToken));
     assert(optional('(', leftParenthesis));
 
-    if (awaitToken != null) {
-      reportRecoverableError(awaitToken, fasta.messageInvalidAwaitFor);
-    }
     Token leftSeparator = ensureSemicolon(token);
     if (optional(';', leftSeparator.next)) {
       token = parseEmptyStatement(leftSeparator);
@@ -5459,15 +5465,16 @@
   /// keyword.
   ///
   /// ```
-  /// forLoopParts:
-  ///   forInitializerStatement expression? ';' expressionList? |
-  ///   declaredIdentifier 'in' expression |
-  ///   identifier 'in' expression
+  ///  forLoopParts:
+  ///      localVariableDeclaration ';' expression? ';' expressionList?
+  ///    | expression? ';' expression? ';' expressionList?
+  ///    | localVariableDeclaration 'in' expression
+  ///    | identifier 'in' expression
   /// ;
   /// ```
   Token parseForInRest(
       Token token, Token awaitToken, Token forToken, Token identifier) {
-    token = parseForInLoopPartsRest(forToken, token, identifier, awaitToken);
+    token = parseForInLoopPartsRest(token, awaitToken, forToken, identifier);
     listener.beginForInBody(token.next);
     LoopState savedLoopState = loopState;
     loopState = LoopState.InsideLoop;
@@ -5479,23 +5486,11 @@
   }
 
   Token parseForInLoopPartsRest(
-      Token forToken, Token token, Token identifier, Token awaitToken) {
+      Token token, Token awaitToken, Token forToken, Token identifier) {
+    Token inKeyword = token.next;
     assert(optional('for', forToken));
     assert(optional('(', forToken.next));
-
-    Token inKeyword = token.next;
-    if (!optional('in', inKeyword)) {
-      // Recovery
-      if (optional(':', inKeyword)) {
-        reportRecoverableError(inKeyword, fasta.messageColonInPlaceOfIn);
-      } else {
-        reportRecoverableError(
-            inKeyword, fasta.templateExpectedButGot.withArguments('in'));
-        inKeyword = token.setNext(
-            new SyntheticKeywordToken(Keyword.IN, inKeyword.offset)
-              ..setNext(inKeyword));
-      }
-    }
+    assert(optional('in', inKeyword) || optional(':', inKeyword));
 
     if (!identifier.isIdentifier) {
       reportRecoverableErrorWithToken(
diff --git a/pkg/front_end/lib/src/fasta/source/type_promotion_look_ahead_listener.dart b/pkg/front_end/lib/src/fasta/source/type_promotion_look_ahead_listener.dart
index 8bc01fa..398cabc 100644
--- a/pkg/front_end/lib/src/fasta/source/type_promotion_look_ahead_listener.dart
+++ b/pkg/front_end/lib/src/fasta/source/type_promotion_look_ahead_listener.dart
@@ -377,6 +377,18 @@
   }
 
   @override
+  void endForControlFlow(Token rightParenthesis) {
+    // TODO(danrubel) add support for for control flow collection entries
+    // but for now this is ignored and an error reported in the body builder.
+  }
+
+  @override
+  void endForInControlFlow(Token rightParenthesis) {
+    // TODO(danrubel) add support for for control flow collection entries
+    // but for now this is ignored and an error reported in the body builder.
+  }
+
+  @override
   void endIfControlFlow(Token token) {
     // TODO(danrubel) add support for if control flow collection entries
     // but for now this is ignored and an error reported in the body builder.
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 380347f..9ae64ae 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
@@ -4,6 +4,7 @@
 
 import 'package:front_end/src/fasta/messages.dart';
 import 'package:front_end/src/fasta/parser.dart';
+import 'package:front_end/src/fasta/parser/async_modifier.dart';
 import 'package:front_end/src/fasta/scanner.dart';
 import 'package:front_end/src/scanner/token.dart';
 import 'package:test/test.dart';
@@ -58,6 +59,138 @@
     );
   }
 
+  test_for() {
+    parseEntry(
+      'before for (var i = 0; i < 10; ++i) 2',
+      [
+        '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',
+        'handleLiteralInt 2',
+        'endForControlFlow 2',
+      ],
+    );
+  }
+
+  test_forIn() {
+    parseEntry(
+      'before await for (var x in y) 2',
+      [
+        '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',
+        'handleLiteralInt 2',
+        'endForInControlFlow 2',
+      ],
+      inAsync: true,
+    );
+  }
+
+  test_forInSpread() {
+    parseEntry(
+      'before for (var x in y) ...[2]',
+      [
+        'beginForControlFlow null 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 null for ( in',
+        'handleNoTypeArguments [',
+        'handleLiteralInt 2',
+        'handleLiteralList 1, [, null, ]',
+        'handleSpreadExpression ...',
+        'endForInControlFlow ]',
+      ],
+    );
+  }
+
+  test_forSpreadQ() {
+    parseEntry(
+      'before for (i = 0; i < 10; ++i) ...[2]',
+      [
+        'beginForControlFlow null for',
+        'handleIdentifier i expression',
+        'handleNoTypeArguments =',
+        'handleNoArguments =',
+        'handleSend i =',
+        'handleLiteralInt 0',
+        'handleAssignmentExpression =',
+        'handleForInitializerExpressionStatement 0',
+        'handleIdentifier i expression',
+        'handleNoTypeArguments <',
+        'handleNoArguments <',
+        'handleSend i <',
+        'beginBinaryExpression <',
+        'handleLiteralInt 10',
+        'endBinaryExpression <',
+        'handleExpressionStatement ;',
+        'handleIdentifier i expression',
+        'handleNoTypeArguments )',
+        'handleNoArguments )',
+        'handleSend i )',
+        'handleUnaryPrefixAssignmentExpression ++',
+        'handleForInitializerExpressionStatement for ( ; 1',
+        'handleNoTypeArguments [',
+        'handleLiteralInt 2',
+        'handleLiteralList 1, [, null, ]',
+        'handleSpreadExpression ...',
+        'endForControlFlow ]',
+      ],
+    );
+  }
+
   test_if() {
     parseEntry(
       'before if (true) 2',
@@ -150,14 +283,20 @@
   }
 
   void parseEntry(String source, List<String> expectedCalls,
-      {List<ExpectedError> errors, String expectAfter}) {
+      {bool inAsync, List<ExpectedError> errors, String expectAfter}) {
     final start = scanString(source).tokens;
     final listener = new TestInfoListener();
     final parser = new Parser(listener);
+    if (inAsync != null) parser.asyncState = AsyncModifier.Async;
     final lastConsumed = parser.parseListOrSetLiteralEntry(start);
 
     expect(listener.errors, errors);
-    expect(listener.calls, expectedCalls, reason: source);
+    try {
+      expect(listener.calls, expectedCalls, reason: source);
+    } catch (e) {
+      listener.calls.forEach((line) => print("  '$line',"));
+      throw e;
+    }
     if (expectAfter != null) {
       expect(lastConsumed.next.lexeme, expectAfter);
     } else {
@@ -231,6 +370,146 @@
     );
   }
 
+  test_for() {
+    parseEntry(
+      'before for (var i = 0; i < 10; ++i) 2:3',
+      [
+        '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',
+        'handleLiteralInt 2',
+        'handleLiteralInt 3',
+        'handleLiteralMapEntry :, ',
+        'endForControlFlow 3',
+      ],
+    );
+  }
+
+  test_forIn() {
+    parseEntry(
+      'before await for (var x in y) 2:3',
+      [
+        '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',
+        'handleLiteralInt 2',
+        'handleLiteralInt 3',
+        'handleLiteralMapEntry :, ',
+        'endForInControlFlow 3',
+      ],
+      inAsync: true,
+    );
+  }
+
+  test_forInSpread() {
+    parseEntry(
+      'before for (var x in y) ...{2:3}',
+      [
+        'beginForControlFlow null 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 null for ( in',
+        'handleNoTypeArguments {',
+        'handleLiteralInt 2',
+        'handleLiteralInt 3',
+        'handleLiteralMapEntry :, }',
+        'handleLiteralMap 1, {, null, }',
+        'handleSpreadExpression ...',
+        'endForInControlFlow }',
+      ],
+    );
+  }
+
+  test_forSpreadQ() {
+    parseEntry(
+      'before for (i = 0; i < 10; ++i) ...?{2:7}',
+      [
+        'beginForControlFlow null for',
+        'handleIdentifier i expression',
+        'handleNoTypeArguments =',
+        'handleNoArguments =',
+        'handleSend i =',
+        'handleLiteralInt 0',
+        'handleAssignmentExpression =',
+        'handleForInitializerExpressionStatement 0',
+        'handleIdentifier i expression',
+        'handleNoTypeArguments <',
+        'handleNoArguments <',
+        'handleSend i <',
+        'beginBinaryExpression <',
+        'handleLiteralInt 10',
+        'endBinaryExpression <',
+        'handleExpressionStatement ;',
+        'handleIdentifier i expression',
+        'handleNoTypeArguments )',
+        'handleNoArguments )',
+        'handleSend i )',
+        'handleUnaryPrefixAssignmentExpression ++',
+        'handleForInitializerExpressionStatement for ( ; 1',
+        'handleNoTypeArguments {',
+        'handleLiteralInt 2',
+        'handleLiteralInt 7',
+        'handleLiteralMapEntry :, }',
+        'handleLiteralMap 1, {, null, }',
+        'handleSpreadExpression ...?',
+        'endForControlFlow }',
+      ],
+    );
+  }
+
   test_if() {
     parseEntry(
       'before if (true) 2:3',
@@ -299,14 +578,20 @@
   }
 
   void parseEntry(String source, List<String> expectedCalls,
-      {List<ExpectedError> errors, String expectAfter}) {
+      {bool inAsync, List<ExpectedError> errors, String expectAfter}) {
     final start = scanString(source).tokens;
     final listener = new TestInfoListener();
     final parser = new Parser(listener);
+    if (inAsync != null) parser.asyncState = AsyncModifier.Async;
     final lastConsumed = parser.parseMapLiteralEntry(start);
 
     expect(listener.errors, errors);
-    expect(listener.calls, expectedCalls, reason: source);
+    try {
+      expect(listener.calls, expectedCalls, reason: source);
+    } catch (e) {
+      listener.calls.forEach((line) => print("  '$line',"));
+      throw e;
+    }
     if (expectAfter != null) {
       expect(lastConsumed.next.lexeme, expectAfter);
     } else {
@@ -320,21 +605,76 @@
   List<ExpectedError> errors;
 
   @override
+  void beginBinaryExpression(Token token) {
+    calls.add('beginBinaryExpression $token');
+  }
+
+  @override
   void beginConstLiteral(Token token) {
     calls.add('beginConstLiteral $token');
   }
 
   @override
+  void beginForControlFlow(Token awaitToken, Token forToken) {
+    calls.add('beginForControlFlow $awaitToken $forToken');
+  }
+
+  @override
+  void beginForInExpression(Token token) {
+    calls.add('beginForInExpression $token');
+  }
+
+  @override
   void beginIfControlFlow(Token ifToken) {
     calls.add('beginIfControlFlow $ifToken');
   }
 
   @override
+  void beginInitializedIdentifier(Token token) {
+    calls.add('beginInitializedIdentifier $token');
+  }
+
+  @override
+  void beginMetadataStar(Token token) {
+    calls.add('beginMetadataStar $token');
+  }
+
+  @override
+  void beginVariablesDeclaration(Token token, Token varFinalOrConst) {
+    calls.add('beginVariablesDeclaration $token $varFinalOrConst');
+  }
+
+  @override
+  void beginVariableInitializer(Token token) {
+    calls.add('beginVariableInitializer $token');
+  }
+
+  @override
+  void endBinaryExpression(Token token) {
+    calls.add('endBinaryExpression $token');
+  }
+
+  @override
   void endConstLiteral(Token token) {
     calls.add('endConstLiteral $token');
   }
 
   @override
+  void endForControlFlow(Token rightParenthesis) {
+    calls.add('endForControlFlow $rightParenthesis');
+  }
+
+  @override
+  void endForInControlFlow(Token rightParenthesis) {
+    calls.add('endForInControlFlow $rightParenthesis');
+  }
+
+  @override
+  void endForInExpression(Token token) {
+    calls.add('endForInExpression $token');
+  }
+
+  @override
   void endIfControlFlow(Token token) {
     calls.add('endIfControlFlow $token');
   }
@@ -345,11 +685,65 @@
   }
 
   @override
+  void endInitializedIdentifier(Token nameToken) {
+    calls.add('endInitializedIdentifier $nameToken');
+  }
+
+  @override
+  void endMetadataStar(int count) {
+    calls.add('endMetadataStar $count');
+  }
+
+  @override
+  void endVariablesDeclaration(int count, Token endToken) {
+    calls.add('endVariablesDeclaration $count $endToken');
+  }
+
+  @override
+  void endVariableInitializer(Token assignmentOperator) {
+    calls.add('endVariableInitializer $assignmentOperator');
+  }
+
+  @override
+  void handleAssignmentExpression(Token token) {
+    calls.add('handleAssignmentExpression $token');
+  }
+
+  @override
   void handleElseControlFlow(Token elseToken) {
     calls.add('handleElseControlFlow $elseToken');
   }
 
   @override
+  void handleExpressionStatement(Token token) {
+    calls.add('handleExpressionStatement $token');
+  }
+
+  @override
+  void handleForInitializerExpressionStatement(Token token) {
+    calls.add('handleForInitializerExpressionStatement $token');
+  }
+
+  @override
+  void handleForInitializerLocalVariableDeclaration(Token token) {
+    calls.add('handleForInitializerLocalVariableDeclaration $token');
+  }
+
+  @override
+  void handleForInLoopParts(Token awaitToken, Token forToken,
+      Token leftParenthesis, Token inKeyword) {
+    calls.add('handleForInLoopParts '
+        '$awaitToken $forToken $leftParenthesis $inKeyword');
+  }
+
+  @override
+  void handleForLoopParts(Token forKeyword, Token leftParen,
+      Token leftSeparator, int updateExpressionCount) {
+    calls.add('handleForInitializerExpressionStatement '
+        '$forKeyword $leftParen $leftSeparator $updateExpressionCount');
+  }
+
+  @override
   void handleIdentifier(Token token, IdentifierContext context) {
     calls.add('handleIdentifier $token $context');
   }
@@ -400,11 +794,21 @@
   }
 
   @override
+  void handleNoType(Token lastConsumed) {
+    calls.add('handleNoTypeArguments $lastConsumed');
+  }
+
+  @override
   void handleNoTypeArguments(Token token) {
     calls.add('handleNoTypeArguments $token');
   }
 
   @override
+  void handleNoVariableInitializer(Token token) {
+    calls.add('handleNoVariableInitializer $token');
+  }
+
+  @override
   void handleRecoverableError(
       Message message, Token startToken, Token endToken) {
     errors ??= <ExpectedError>[];
@@ -422,6 +826,11 @@
     calls.add('handleSpreadExpression $spreadToken');
   }
 
+  @override
+  void handleUnaryPrefixAssignmentExpression(Token token) {
+    calls.add('handleUnaryPrefixAssignmentExpression $token');
+  }
+
   noSuchMethod(Invocation invocation) {
     throw '${invocation.memberName} should not be called.';
   }