Parse spread collections in map literals

This CL adds support for parsing spread collection entries in map literals.
For now, all listeners report an error on the spread collection tokens
'...' and '...?' until support has been added for those listeners.

Change-Id: I299bb1524fda39746471c07e618df6a2305b1a2e
Reviewed-on: https://dart-review.googlesource.com/c/89860
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Dan Rubel <danrubel@google.com>
diff --git a/pkg/analyzer/lib/src/fasta/ast_builder.dart b/pkg/analyzer/lib/src/fasta/ast_builder.dart
index 2ffe897..2d799a8 100644
--- a/pkg/analyzer/lib/src/fasta/ast_builder.dart
+++ b/pkg/analyzer/lib/src/fasta/ast_builder.dart
@@ -906,13 +906,12 @@
   }
 
   @override
-  void handleEmptyLiteralSetOrMap(
-      Token leftBrace, Token constKeyword, Token rightBrace) {
+  void handleLiteralSetOrMap(
+      int count, Token leftBrace, Token constKeyword, Token rightBrace) {
     // TODO(danrubel): From a type resolution standpoint, this could be either
     // a set literal or a map literal depending upon the context
     // in which this expression occurs.
-    // For now, generate a map literal.
-    handleLiteralMap(0, leftBrace, constKeyword, rightBrace);
+    handleLiteralMap(count, leftBrace, constKeyword, rightBrace);
   }
 
   void handleLiteralSet(
@@ -935,7 +934,16 @@
     assert(optional('}', rightBracket));
     debugEvent("LiteralMap");
 
-    List<MapLiteralEntry> entries = popTypedList(count) ?? <MapLiteralEntry>[];
+    // TODO(danrubel): Revise once spread collection AST structures
+    // are in place. For now, drop those on the floor
+    // as error(s) have already been reported in handleSpreadExpression.
+    List<MapLiteralEntry> entries = <MapLiteralEntry>[];
+    popTypedList(count)?.forEach((entry) {
+      if (entry is MapLiteralEntry) {
+        entries.add(entry);
+      }
+    });
+
     TypeArgumentList typeArguments = pop();
     push(ast.mapLiteral(
         constKeyword, typeArguments, leftBracket, entries, rightBracket));
diff --git a/pkg/analyzer/test/generated/parser_fasta_test.dart b/pkg/analyzer/test/generated/parser_fasta_test.dart
index b4e158e..6b798e1 100644
--- a/pkg/analyzer/test/generated/parser_fasta_test.dart
+++ b/pkg/analyzer/test/generated/parser_fasta_test.dart
@@ -308,6 +308,109 @@
     expect(value.value, 6);
   }
 
+  void test_mapLiteral_invalid_set_entry() {
+    MapLiteral map =
+        parseExpression('<int, int>{1}', parseSetLiterals: true, errors: [
+      expectedError(ParserErrorCode.EXPECTED_TOKEN, 12, 1),
+      expectedError(ParserErrorCode.MISSING_IDENTIFIER, 12, 1),
+    ]);
+    expect(map.constKeyword, isNull);
+    expect(map.typeArguments.arguments, hasLength(2));
+    expect(map.entries, hasLength(1));
+  }
+
+  @failingTest
+  void test_mapLiteral_invalid_too_many_type_arguments1() {
+    MapLiteral map =
+        parseExpression('<int, int, int>{}', parseSetLiterals: true, errors: [
+      // TODO(danrubel): Currently the resolver reports invalid number of
+      // type arguments, but the parser could report this.
+      expectedError(
+          /* ParserErrorCode.EXPECTED_ONE_OR_TWO_TYPE_VARIABLES */
+          ParserErrorCode.EXPECTED_TOKEN,
+          11,
+          3),
+    ]);
+    expect(map.constKeyword, isNull);
+    expect(map.entries, hasLength(0));
+  }
+
+  @failingTest
+  void test_mapLiteral_invalid_too_many_type_arguments2() {
+    MapLiteral map =
+        parseExpression('<int, int, int>{1}', parseSetLiterals: true, errors: [
+      // TODO(danrubel): Currently the resolver reports invalid number of
+      // type arguments, but the parser could report this.
+      expectedError(
+          /* ParserErrorCode.EXPECTED_ONE_OR_TWO_TYPE_VARIABLES */
+          ParserErrorCode.EXPECTED_TOKEN,
+          11,
+          3),
+    ]);
+    expect(map.constKeyword, isNull);
+    expect(map.entries, hasLength(0));
+  }
+
+  void test_mapLiteral_spread() {
+    // TODO(danrubel): Revise this once AST supports new syntax
+    MapLiteral map = parseExpression('{1: 2, ...{3: 4}}',
+        parseSetLiterals: true,
+        errors: [expectedError(ParserErrorCode.UNEXPECTED_TOKEN, 7, 3)]);
+    expect(map.constKeyword, isNull);
+    expect(map.typeArguments, isNull);
+    expect(map.entries, hasLength(1));
+  }
+
+  void test_mapLiteral_spreadQ() {
+    // TODO(danrubel): Revise this once AST supports new syntax
+    MapLiteral map = parseExpression('{1: 2, ...?{3: 4}}',
+        parseSetLiterals: true,
+        errors: [expectedError(ParserErrorCode.UNEXPECTED_TOKEN, 7, 4)]);
+    expect(map.constKeyword, isNull);
+    expect(map.typeArguments, isNull);
+    expect(map.entries, hasLength(1));
+  }
+
+  void test_mapLiteral_spread_typed() {
+    // TODO(danrubel): Revise this once AST supports new syntax
+    MapLiteral map = parseExpression('<int, int>{...{3: 4}}',
+        parseSetLiterals: true,
+        errors: [expectedError(ParserErrorCode.UNEXPECTED_TOKEN, 11, 3)]);
+    expect(map.constKeyword, isNull);
+    expect(map.typeArguments.arguments, hasLength(2));
+    expect(map.entries, hasLength(0));
+  }
+
+  void test_mapLiteral_spreadQ_typed() {
+    // TODO(danrubel): Revise this once AST supports new syntax
+    MapLiteral map = parseExpression('<int, int>{...?{3: 4}}',
+        parseSetLiterals: true,
+        errors: [expectedError(ParserErrorCode.UNEXPECTED_TOKEN, 11, 4)]);
+    expect(map.constKeyword, isNull);
+    expect(map.typeArguments.arguments, hasLength(2));
+    expect(map.entries, hasLength(0));
+  }
+
+  void test_mapLiteral_spread2_typed() {
+    // TODO(danrubel): Revise this once AST supports new syntax
+    MapLiteral map = parseExpression('<int, int>{1: 2, ...{3: 4}}',
+        parseSetLiterals: true,
+        errors: [expectedError(ParserErrorCode.UNEXPECTED_TOKEN, 17, 3)]);
+    expect(map.constKeyword, isNull);
+    expect(map.typeArguments.arguments, hasLength(2));
+    expect(map.entries, hasLength(1));
+  }
+
+  void test_mapLiteral_spreadQ2_typed() {
+    // TODO(danrubel): Revise this once AST supports new syntax
+    MapLiteral map = parseExpression('<int, int>{1: 2, ...?{3: 4}}',
+        parseSetLiterals: true,
+        errors: [expectedError(ParserErrorCode.UNEXPECTED_TOKEN, 17, 4)]);
+    expect(map.constKeyword, isNull);
+    expect(map.typeArguments.arguments, hasLength(2));
+    expect(map.entries, hasLength(1));
+  }
+
   void test_setLiteral() {
     SetLiteral set = parseExpression('{3}', parseSetLiterals: true);
     expect(set.constKeyword, isNull);
@@ -328,28 +431,43 @@
     expect(value2.value, 6);
   }
 
-  void test_setLiteral_spread_typed() {
-    // TODO(danrubel): Revise this once AST supports new syntax
-    SetLiteral set = parseExpression('<int>{...[3]}',
-        parseSetLiterals: true,
-        errors: [expectedError(ParserErrorCode.UNEXPECTED_TOKEN, 6, 3)]);
-    expect(set.constKeyword, isNull);
-    expect(set.typeArguments, isNotNull);
-    expect(set.elements, hasLength(1));
-    ListLiteral list = set.elements[0];
-    expect(list.elements, hasLength(1));
+  void test_setLiteral_const_typed() {
+    SetLiteral set = parseExpression('const <int>{3}', parseSetLiterals: true);
+    expect(set.constKeyword, isNotNull);
+    expect(set.typeArguments.arguments, hasLength(1));
+    NamedType typeArg = set.typeArguments.arguments[0];
+    expect(typeArg.name.name, 'int');
+    expect(set.elements.length, 1);
+    IntegerLiteral value = set.elements[0];
+    expect(value.value, 3);
   }
 
-  void test_setLiteral_spreadQ_typed() {
-    // TODO(danrubel): Revise this once AST supports new syntax
-    SetLiteral set = parseExpression('<int>{...?[3]}',
-        parseSetLiterals: true,
-        errors: [expectedError(ParserErrorCode.UNEXPECTED_TOKEN, 6, 4)]);
+  void test_setLiteral_invalid_map_entry() {
+    SetLiteral set =
+        parseExpression('<int>{1: 1}', parseSetLiterals: true, errors: [
+      expectedError(ParserErrorCode.EXPECTED_TOKEN, 7, 1),
+    ]);
     expect(set.constKeyword, isNull);
-    expect(set.typeArguments, isNotNull);
-    expect(set.elements, hasLength(1));
-    ListLiteral list = set.elements[0];
-    expect(list.elements, hasLength(1));
+    expect(set.typeArguments.arguments, hasLength(1));
+    NamedType typeArg = set.typeArguments.arguments[0];
+    expect(typeArg.name.name, 'int');
+    expect(set.elements.length, 1);
+  }
+
+  void test_setLiteral_nested_typeArgument() {
+    SetLiteral set = parseExpression('<Set<int>>{{3}}', parseSetLiterals: true);
+    expect(set.constKeyword, isNull);
+    expect(set.typeArguments.arguments, hasLength(1));
+    NamedType typeArg1 = set.typeArguments.arguments[0];
+    expect(typeArg1.name.name, 'Set');
+    expect(typeArg1.typeArguments.arguments, hasLength(1));
+    NamedType typeArg2 = typeArg1.typeArguments.arguments[0];
+    expect(typeArg2.name.name, 'int');
+    expect(set.elements.length, 1);
+    SetLiteral intSet = set.elements[0];
+    expect(intSet.elements, hasLength(1));
+    IntegerLiteral value = intSet.elements[0];
+    expect(value.value, 3);
   }
 
   void test_setLiteral_spread2() {
@@ -380,79 +498,31 @@
     expect(list.elements, hasLength(1));
   }
 
-  void test_setLiteral_const_typeArgument() {
-    SetLiteral set = parseExpression('const <int>{3}', parseSetLiterals: true);
-    expect(set.constKeyword, isNotNull);
-    expect(set.typeArguments.arguments, hasLength(1));
-    NamedType typeArg = set.typeArguments.arguments[0];
-    expect(typeArg.name.name, 'int');
-    expect(set.elements.length, 1);
-    IntegerLiteral value = set.elements[0];
-    expect(value.value, 3);
-  }
-
-  void test_setLiteral_invalid_map_entry() {
-    parseExpression('<int>{1: 1}', parseSetLiterals: true, errors: [
-      expectedError(ParserErrorCode.EXPECTED_TOKEN, 7, 1),
-    ]);
-  }
-
-  @failingTest
-  void test_setLiteral_invalid_too_many_type_arguments1() {
-    parseExpression('<int, int, int>{}', parseSetLiterals: true, errors: [
-      // TODO(danrubel): Currently the resolver reports invalid number of
-      // type arguments, but the parser could report this.
-      expectedError(
-          /* ParserErrorCode.EXPECTED_ONE_TYPE_VARIABLE */
-          ParserErrorCode.EXPECTED_TOKEN,
-          15,
-          1),
-    ]);
-  }
-
-  @failingTest
-  void test_setLiteral_invalid_too_many_type_arguments2() {
-    parseExpression('<int, int, int>{1}', parseSetLiterals: true, errors: [
-      // TODO(danrubel): Currently the resolver reports invalid number of
-      // type arguments, but the parser could report this.
-      expectedError(
-          /* ParserErrorCode.EXPECTED_ONE_TYPE_VARIABLE */
-          ParserErrorCode.EXPECTED_TOKEN,
-          15,
-          1),
-    ]);
-  }
-
-  @failingTest
-  void test_setLiteral_invalid_too_many_type_arguments3() {
-    parseExpression('<int, int>{1}', parseSetLiterals: true, errors: [
-      // TODO(danrubel): Currently the resolver reports invalid number of
-      // type arguments, but the parser could report this.
-      expectedError(
-          /* ParserErrorCode.EXPECTED_ONE_TYPE_VARIABLE */
-          ParserErrorCode.EXPECTED_TOKEN,
-          10,
-          1),
-    ]);
-  }
-
-  void test_setLiteral_nested_typeArgument() {
-    SetLiteral set = parseExpression('<Set<int>>{{3}}', parseSetLiterals: true);
+  void test_setLiteral_spread_typed() {
+    // TODO(danrubel): Revise this once AST supports new syntax
+    SetLiteral set = parseExpression('<int>{...[3]}',
+        parseSetLiterals: true,
+        errors: [expectedError(ParserErrorCode.UNEXPECTED_TOKEN, 6, 3)]);
     expect(set.constKeyword, isNull);
-    expect(set.typeArguments.arguments, hasLength(1));
-    NamedType typeArg1 = set.typeArguments.arguments[0];
-    expect(typeArg1.name.name, 'Set');
-    expect(typeArg1.typeArguments.arguments, hasLength(1));
-    NamedType typeArg2 = typeArg1.typeArguments.arguments[0];
-    expect(typeArg2.name.name, 'int');
-    expect(set.elements.length, 1);
-    SetLiteral intSet = set.elements[0];
-    expect(intSet.elements, hasLength(1));
-    IntegerLiteral value = intSet.elements[0];
-    expect(value.value, 3);
+    expect(set.typeArguments, isNotNull);
+    expect(set.elements, hasLength(1));
+    ListLiteral list = set.elements[0];
+    expect(list.elements, hasLength(1));
   }
 
-  void test_setLiteral_typeArgument() {
+  void test_setLiteral_spreadQ_typed() {
+    // TODO(danrubel): Revise this once AST supports new syntax
+    SetLiteral set = parseExpression('<int>{...?[3]}',
+        parseSetLiterals: true,
+        errors: [expectedError(ParserErrorCode.UNEXPECTED_TOKEN, 6, 4)]);
+    expect(set.constKeyword, isNull);
+    expect(set.typeArguments, isNotNull);
+    expect(set.elements, hasLength(1));
+    ListLiteral list = set.elements[0];
+    expect(list.elements, hasLength(1));
+  }
+
+  void test_setLiteral_typed() {
     SetLiteral set = parseExpression('<int>{3}', parseSetLiterals: true);
     expect(set.constKeyword, isNull);
     expect(set.typeArguments.arguments, hasLength(1));
@@ -462,6 +532,26 @@
     IntegerLiteral value = set.elements[0];
     expect(value.value, 3);
   }
+
+  void test_setOrMapLiteral_spread() {
+    // TODO(danrubel): Revise this once AST supports new syntax
+    MapLiteral set = parseExpression('{...{3: 4}}',
+        parseSetLiterals: true,
+        errors: [expectedError(ParserErrorCode.UNEXPECTED_TOKEN, 1, 3)]);
+    expect(set.constKeyword, isNull);
+    expect(set.typeArguments, isNull);
+    expect(set.entries, hasLength(0));
+  }
+
+  void test_setOrMapLiteral_spreadQ() {
+    // TODO(danrubel): Revise this once AST supports new syntax
+    MapLiteral set = parseExpression('{...?{3: 4}}',
+        parseSetLiterals: true,
+        errors: [expectedError(ParserErrorCode.UNEXPECTED_TOKEN, 1, 4)]);
+    expect(set.constKeyword, isNull);
+    expect(set.typeArguments, isNull);
+    expect(set.entries, hasLength(0));
+  }
 }
 
 /**
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 ae4004e..864339b 100644
--- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
@@ -2368,35 +2368,11 @@
   }
 
   @override
-  void handleEmptyLiteralSetOrMap(
-      Token leftBrace, Token constKeyword, Token rightBrace) {
-    debugEvent("EmptyLiteralSetOrMap");
+  void handleLiteralSetOrMap(
+      int count, Token leftBrace, Token constKeyword, Token rightBrace) {
+    debugEvent("LiteralSetOrMap");
     // Treat as map literal - type inference will find the right type.
-    List<UnresolvedType<KernelTypeBuilder>> typeArguments = pop();
-    assert(typeArguments == null || typeArguments.length > 2);
-    if (typeArguments != null && typeArguments.length > 2) {
-      if (library.loader.target.enableSetLiterals) {
-        addProblem(
-            fasta.messageSetOrMapLiteralTooManyTypeArguments,
-            offsetForToken(leftBrace),
-            lengthOfSpan(leftBrace, leftBrace.endGroup));
-      } else {
-        addProblem(
-            fasta.messageMapLiteralTypeArgumentMismatch,
-            offsetForToken(leftBrace),
-            lengthOfSpan(leftBrace, leftBrace.endGroup));
-      }
-    }
-    DartType implicitTypeArgument = this.implicitTypeArgument;
-    push(forest.literalMap(
-        constKeyword,
-        constKeyword != null || constantContext == ConstantContext.inferred,
-        implicitTypeArgument,
-        implicitTypeArgument,
-        null,
-        leftBrace,
-        <MapEntry>[],
-        rightBrace));
+    handleLiteralMap(count, leftBrace, constKeyword, rightBrace);
   }
 
   @override
@@ -2423,8 +2399,17 @@
   void handleLiteralMap(
       int count, Token leftBrace, Token constKeyword, Token rightBrace) {
     debugEvent("LiteralMap");
-    List<MapEntry> entries =
-        const GrowableList<MapEntry>().pop(stack, count) ?? <MapEntry>[];
+
+    // TODO(danrubel): Revise once spread collection entries are supported.
+    // For now, drop those on the floor
+    // as error(s) have already been reported in handleSpreadExpression.
+    List<MapEntry> entries = <MapEntry>[];
+    const FixedNullableList<dynamic>().pop(stack, count)?.forEach((entry) {
+      if (entry is MapEntry) {
+        entries.add(entry);
+      }
+    });
+
     List<UnresolvedType<KernelTypeBuilder>> typeArguments = pop();
     DartType keyType;
     DartType valueType;
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 f447023..68fc71c 100644
--- a/pkg/front_end/lib/src/fasta/parser/forwarding_listener.dart
+++ b/pkg/front_end/lib/src/fasta/parser/forwarding_listener.dart
@@ -981,9 +981,9 @@
   }
 
   @override
-  void handleEmptyLiteralSetOrMap(
-      Token leftBrace, Token constKeyword, Token rightBrace) {
-    listener?.handleEmptyLiteralSetOrMap(leftBrace, constKeyword, rightBrace);
+  void handleLiteralSetOrMap(
+      int count, Token leftBrace, Token constKeyword, Token rightBrace) {
+    listener?.handleLiteralSetOrMap(count, leftBrace, constKeyword, rightBrace);
   }
 
   @override
diff --git a/pkg/front_end/lib/src/fasta/parser/listener.dart b/pkg/front_end/lib/src/fasta/parser/listener.dart
index dc31098..4b1a823 100644
--- a/pkg/front_end/lib/src/fasta/parser/listener.dart
+++ b/pkg/front_end/lib/src/fasta/parser/listener.dart
@@ -1163,9 +1163,9 @@
     logEvent("LiteralSet");
   }
 
-  void handleEmptyLiteralSetOrMap(
-      Token leftBrace, Token constKeyword, Token rightBrace) {
-    logEvent('EmptyLiteralSetOrMap');
+  void handleLiteralSetOrMap(
+      int count, Token leftBrace, Token constKeyword, Token rightBrace) {
+    logEvent('LiteralSetOrMap');
   }
 
   void handleLiteralNull(Token token) {
diff --git a/pkg/front_end/lib/src/fasta/parser/parser.dart b/pkg/front_end/lib/src/fasta/parser/parser.dart
index 48e16d1..313836e 100644
--- a/pkg/front_end/lib/src/fasta/parser/parser.dart
+++ b/pkg/front_end/lib/src/fasta/parser/parser.dart
@@ -4186,7 +4186,7 @@
         token = next;
         break;
       }
-      token = parseSpreadExpressionOrExpression(token);
+      token = parseSpreadableExpression(token);
       next = token.next;
       ++count;
       if (!optional(',', next)) {
@@ -4223,10 +4223,10 @@
     return token;
   }
 
-  Token parseSpreadExpressionOrExpression(Token token) {
+  Token parseSpreadableExpression(Token token) {
     Token next = token.next;
     if (optional('...', next) || optional('...?', next)) {
-      token = parseExpression(token.next);
+      token = parseExpression(next);
       listener.handleSpreadExpression(next);
     } else {
       token = parseExpression(token);
@@ -4244,23 +4244,34 @@
 
     Token leftBrace = start.next;
     assert(optional('{', leftBrace));
-    if (optional('}', leftBrace.next)) {
-      Token rightBrace = leftBrace.next;
-      listener.handleEmptyLiteralSetOrMap(leftBrace, constKeyword, rightBrace);
-      return rightBrace;
+
+    Token token = leftBrace;
+    Token next = token.next;
+    int count = 0;
+    while (optional('...', next) || optional('...?', next)) {
+      token = parseExpression(next);
+      listener.handleSpreadExpression(next);
+      next = token.next;
+      ++count;
+    }
+
+    if (optional('}', next)) {
+      listener.handleLiteralSetOrMap(count, leftBrace, constKeyword, next);
+      return next;
     }
 
     bool old = mayParseFunctionExpressions;
     mayParseFunctionExpressions = true;
-    Token token = parseExpression(leftBrace);
+    token = parseExpression(token);
+    ++count;
     mayParseFunctionExpressions = old;
 
-    Token next = token.next;
+    next = token.next;
     if (optional('}', next)) {
-      listener.handleLiteralSet(1, leftBrace, constKeyword, next);
+      listener.handleLiteralSet(count, leftBrace, constKeyword, next);
       return next;
     } else if (optional(',', next)) {
-      return parseLiteralSetRest(token, constKeyword, leftBrace);
+      return parseLiteralSetRest(token, count, constKeyword, leftBrace);
     } else {
       // TODO(danrubel): Consider better recovery
       // rather than just assuming this is a literal map.
@@ -4272,7 +4283,7 @@
       mayParseFunctionExpressions = old;
 
       listener.handleLiteralMapEntry(colon, token.next);
-      return parseLiteralMapRest(token, constKeyword, leftBrace);
+      return parseLiteralMapRest(token, count, constKeyword, leftBrace);
     }
   }
 
@@ -4302,12 +4313,12 @@
     token = parseMapLiteralEntry(token);
     mayParseFunctionExpressions = old;
 
-    return parseLiteralMapRest(token, constKeyword, beginToken);
+    return parseLiteralMapRest(token, 1, constKeyword, beginToken);
   }
 
   /// Parse a literal map after the first entry.
-  Token parseLiteralMapRest(Token token, Token constKeyword, Token beginToken) {
-    int count = 1;
+  Token parseLiteralMapRest(
+      Token token, int count, Token constKeyword, Token beginToken) {
     bool old = mayParseFunctionExpressions;
     mayParseFunctionExpressions = true;
     while (true) {
@@ -4375,15 +4386,15 @@
 
     bool old = mayParseFunctionExpressions;
     mayParseFunctionExpressions = true;
-    token = parseSpreadExpressionOrExpression(token);
+    token = parseSpreadableExpression(token);
     mayParseFunctionExpressions = old;
 
-    return parseLiteralSetRest(token, constKeyword, beginToken);
+    return parseLiteralSetRest(token, 1, constKeyword, beginToken);
   }
 
   /// Parse a literal set after the first expression.
-  Token parseLiteralSetRest(Token token, Token constKeyword, Token beginToken) {
-    int count = 1;
+  Token parseLiteralSetRest(
+      Token token, int count, Token constKeyword, Token beginToken) {
     bool old = mayParseFunctionExpressions;
     mayParseFunctionExpressions = true;
     while (true) {
@@ -4414,7 +4425,7 @@
           break;
         }
       }
-      token = parseSpreadExpressionOrExpression(token);
+      token = parseSpreadableExpression(token);
       ++count;
     }
     assert(optional('}', token));
@@ -4488,17 +4499,24 @@
 
   /// ```
   /// mapLiteralEntry:
-  ///   expression ':' expression
+  ///   expression ':' expression |
+  ///   ( '...' | '...?' ) expressionList
   /// ;
   /// ```
   Token parseMapLiteralEntry(Token token) {
     // Assume the listener rejects non-string keys.
     // TODO(brianwilkerson): Change the assumption above by moving error
     // checking into the parser, making it possible to recover.
-    token = parseExpression(token);
-    Token colon = ensureColon(token);
-    token = parseExpression(colon);
-    listener.handleLiteralMapEntry(colon, token.next);
+    Token next = token.next;
+    if (optional('...', next) || optional('...?', next)) {
+      token = parseExpression(next);
+      listener.handleSpreadExpression(next);
+    } else {
+      token = parseExpression(token);
+      Token colon = ensureColon(token);
+      token = parseExpression(colon);
+      listener.handleLiteralMapEntry(colon, token.next);
+    }
     return token;
   }
 
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 97611a1..8db8764 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
@@ -379,7 +379,8 @@
   @override
   void handleSpreadExpression(Token spreadToken) {
     // TODO(danrubel) add support for spread collections
-    // but for now this is ignored and an error reported in the body builder
+    // but for now this is ignored and an error reported in the body builder.
+    // The top of stack is the spread collection expression.
   }
 
   void doConstuctorInvocation(Token token, bool isConst) {
@@ -858,9 +859,10 @@
   }
 
   @override
-  void handleEmptyLiteralSetOrMap(
-      Token leftBrace, Token constKeyword, Token rightBrace) {
-    debugEvent("EmptyLiteralSetOrMap", leftBrace);
+  void handleLiteralSetOrMap(
+      int count, Token leftBrace, Token constKeyword, Token rightBrace) {
+    debugEvent("LiteralSetOrMap", leftBrace);
+    state.discard(count);
     state.pushNull("{}", leftBrace);
   }