Update AstBuilder to build spread collection AST structures

Change-Id: Ifde5047933d810bdda77fe1b46eb6f69b7eabc5a
Reviewed-on: https://dart-review.googlesource.com/c/90082
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Dan Rubel <danrubel@google.com>
diff --git a/pkg/analyzer/lib/dart/ast/ast.dart b/pkg/analyzer/lib/dart/ast/ast.dart
index 5fb2d22..fdb49c8 100644
--- a/pkg/analyzer/lib/dart/ast/ast.dart
+++ b/pkg/analyzer/lib/dart/ast/ast.dart
@@ -5142,7 +5142,7 @@
  *
  *    listLiteral ::=
  *        'const'? ('<' [TypeAnnotation] '>')?
- *        '[' ([CollectionLiteralElement] ','?)? ']'
+ *        '[' ([CollectionElement] ','?)? ']'
  *
  * This is the class that is used to represent a list literal when either the
  * 'control-flow-collections' or 'spread-collections' experiments are enabled.
diff --git a/pkg/analyzer/lib/src/fasta/ast_builder.dart b/pkg/analyzer/lib/src/fasta/ast_builder.dart
index 477b9c1..7c1350e 100644
--- a/pkg/analyzer/lib/src/fasta/ast_builder.dart
+++ b/pkg/analyzer/lib/src/fasta/ast_builder.dart
@@ -279,9 +279,12 @@
 
   @override
   void handleSpreadExpression(Token spreadToken) {
-    // TODO(danrubel): generate new AST structure
-    handleRecoverableError(templateUnexpectedToken.withArguments(spreadToken),
-        spreadToken, spreadToken);
+    if (enableSpreadCollections) {
+      push(ast.spreadElement(spreadOperator: spreadToken, expression: pop()));
+    } else {
+      handleRecoverableError(templateUnexpectedToken.withArguments(spreadToken),
+          spreadToken, spreadToken);
+    }
   }
 
   void handleStringJuxtaposition(int literalCount) {
@@ -865,10 +868,22 @@
     assert(optional(']', rightBracket));
     debugEvent("LiteralList");
 
-    List<Expression> expressions = popTypedList(count);
-    TypeArgumentList typeArguments = pop();
-    push(ast.listLiteral(
-        constKeyword, typeArguments, leftBracket, expressions, rightBracket));
+    if (enableSpreadCollections) {
+      List<CollectionElement> elements = popTypedList(count);
+      TypeArgumentList typeArguments = pop();
+      push(ast.listLiteral2(
+        constKeyword: constKeyword,
+        typeArguments: typeArguments,
+        leftBracket: leftBracket,
+        elements: elements,
+        rightBracket: rightBracket,
+      ));
+    } else {
+      List<Expression> expressions = popTypedList(count);
+      TypeArgumentList typeArguments = pop();
+      push(ast.listLiteral(
+          constKeyword, typeArguments, leftBracket, expressions, rightBracket));
+    }
   }
 
   void handleAsyncModifier(Token asyncToken, Token starToken) {
@@ -927,10 +942,23 @@
     assert(optional('}', rightBracket));
     debugEvent("LiteralSet");
 
-    List<Expression> entries = popTypedList(count) ?? <Expression>[];
-    TypeArgumentList typeArguments = pop();
-    push(ast.setLiteral(
-        constKeyword, typeArguments, leftBracket, entries, rightBracket));
+    if (enableSpreadCollections) {
+      List<CollectionElement> elements =
+          popTypedList(count) ?? <CollectionElement>[];
+      TypeArgumentList typeArguments = pop();
+      push(ast.setLiteral2(
+        constKeyword: constKeyword,
+        typeArguments: typeArguments,
+        leftBracket: leftBracket,
+        elements: elements,
+        rightBracket: rightBracket,
+      ));
+    } else {
+      List<Expression> entries = popTypedList(count) ?? <Expression>[];
+      TypeArgumentList typeArguments = pop();
+      push(ast.setLiteral(
+          constKeyword, typeArguments, leftBracket, entries, rightBracket));
+    }
   }
 
   void handleLiteralMap(
@@ -940,19 +968,34 @@
     assert(optional('}', rightBracket));
     debugEvent("LiteralMap");
 
-    // 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);
-      }
-    });
+    if (enableSpreadCollections) {
+      List<MapElement> entries = <MapElement>[];
+      popTypedList(count)?.forEach((entry) {
+        if (entry is MapElement) {
+          entries.add(entry);
+        }
+      });
 
-    TypeArgumentList typeArguments = pop();
-    push(ast.mapLiteral(
-        constKeyword, typeArguments, leftBracket, entries, rightBracket));
+      TypeArgumentList typeArguments = pop();
+      push(ast.mapLiteral2(
+        constKeyword: constKeyword,
+        typeArguments: typeArguments,
+        leftBracket: leftBracket,
+        entries: entries,
+        rightBracket: rightBracket,
+      ));
+    } else {
+      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));
+    }
   }
 
   void handleLiteralMapEntry(Token colon, Token endToken) {
diff --git a/pkg/analyzer/test/generated/parser_fasta_test.dart b/pkg/analyzer/test/generated/parser_fasta_test.dart
index 6b798e1..779173a 100644
--- a/pkg/analyzer/test/generated/parser_fasta_test.dart
+++ b/pkg/analyzer/test/generated/parser_fasta_test.dart
@@ -40,6 +40,7 @@
     defineReflectiveTests(NNBDParserTest_Fasta);
     defineReflectiveTests(RecoveryParserTest_Fasta);
     defineReflectiveTests(SimpleParserTest_Fasta);
+    defineReflectiveTests(CollectionLiteralParserTest);
     defineReflectiveTests(StatementParserTest_Fasta);
     defineReflectiveTests(TopLevelParserTest_Fasta);
   });
@@ -105,6 +106,196 @@
 }
 
 /**
+ * Tests of the fasta parser based on [ExpressionParserTestMixin].
+ */
+@reflectiveTest
+class CollectionLiteralParserTest extends FastaParserTestCase {
+  Expression parseCollectionLiteral(String source,
+      {List<ErrorCode> codes,
+      List<ExpectedError> errors,
+      int expectedEndOffset}) {
+    return parseExpression(source,
+        codes: codes,
+        errors: errors,
+        expectedEndOffset: expectedEndOffset,
+        parseSetLiterals: true,
+        parseSpreadCollections: true);
+  }
+
+  void test_listLiteral_spread() {
+    ListLiteral2 list = parseCollectionLiteral('[1, ...[2]]');
+    expect(list.elements, hasLength(2));
+    IntegerLiteral first = list.elements[0];
+    expect(first.value, 1);
+
+    SpreadElement element = list.elements[1];
+    expect(element.spreadOperator.lexeme, '...');
+    ListLiteral2 spreadExpression = element.expression;
+    expect(spreadExpression.elements, hasLength(1));
+  }
+
+  void test_listLiteral_spreadQ() {
+    ListLiteral2 list = parseCollectionLiteral('[1, ...?[2]]');
+    expect(list.elements, hasLength(2));
+    IntegerLiteral first = list.elements[0];
+    expect(first.value, 1);
+
+    SpreadElement element = list.elements[1];
+    expect(element.spreadOperator.lexeme, '...?');
+    ListLiteral2 spreadExpression = element.expression;
+    expect(spreadExpression.elements, hasLength(1));
+  }
+
+  void test_mapLiteral_spread() {
+    MapLiteral2 map = parseCollectionLiteral('{1: 2, ...{3: 4}}');
+    expect(map.constKeyword, isNull);
+    expect(map.typeArguments, isNull);
+    expect(map.entries, hasLength(2));
+
+    SpreadElement element = map.entries[1];
+    expect(element.spreadOperator.lexeme, '...');
+    MapLiteral2 spreadExpression = element.expression;
+    expect(spreadExpression.entries, hasLength(1));
+  }
+
+  void test_mapLiteral_spreadQ() {
+    MapLiteral2 map = parseCollectionLiteral('{1: 2, ...?{3: 4}}');
+    expect(map.constKeyword, isNull);
+    expect(map.typeArguments, isNull);
+    expect(map.entries, hasLength(2));
+
+    SpreadElement element = map.entries[1];
+    expect(element.spreadOperator.lexeme, '...?');
+    MapLiteral2 spreadExpression = element.expression;
+    expect(spreadExpression.entries, hasLength(1));
+  }
+
+  void test_mapLiteral_spread_typed() {
+    MapLiteral2 map = parseCollectionLiteral('<int, int>{...{3: 4}}');
+    expect(map.constKeyword, isNull);
+    expect(map.typeArguments.arguments, hasLength(2));
+    expect(map.entries, hasLength(1));
+
+    SpreadElement element = map.entries[0];
+    expect(element.spreadOperator.lexeme, '...');
+    MapLiteral2 spreadExpression = element.expression;
+    expect(spreadExpression.entries, hasLength(1));
+  }
+
+  void test_mapLiteral_spreadQ_typed() {
+    MapLiteral2 map = parseCollectionLiteral('<int, int>{...?{3: 4}}');
+    expect(map.constKeyword, isNull);
+    expect(map.typeArguments.arguments, hasLength(2));
+    expect(map.entries, hasLength(1));
+
+    SpreadElement element = map.entries[0];
+    expect(element.spreadOperator.lexeme, '...?');
+    MapLiteral2 spreadExpression = element.expression;
+    expect(spreadExpression.entries, hasLength(1));
+  }
+
+  void test_mapLiteral_spread2_typed() {
+    MapLiteral2 map = parseCollectionLiteral('<int, int>{1: 2, ...{3: 4}}');
+    expect(map.constKeyword, isNull);
+    expect(map.typeArguments.arguments, hasLength(2));
+    expect(map.entries, hasLength(2));
+
+    SpreadElement element = map.entries[1];
+    expect(element.spreadOperator.lexeme, '...');
+    MapLiteral2 spreadExpression = element.expression;
+    expect(spreadExpression.entries, hasLength(1));
+  }
+
+  void test_mapLiteral_spreadQ2_typed() {
+    MapLiteral2 map = parseCollectionLiteral('<int, int>{1: 2, ...?{3: 4}}');
+    expect(map.constKeyword, isNull);
+    expect(map.typeArguments.arguments, hasLength(2));
+    expect(map.entries, hasLength(2));
+
+    SpreadElement element = map.entries[1];
+    expect(element.spreadOperator.lexeme, '...?');
+    MapLiteral2 spreadExpression = element.expression;
+    expect(spreadExpression.entries, hasLength(1));
+  }
+
+  void test_setLiteral_spread2() {
+    SetLiteral2 set = parseCollectionLiteral('{3, ...[4]}');
+    expect(set.constKeyword, isNull);
+    expect(set.typeArguments, isNull);
+    expect(set.elements, hasLength(2));
+    IntegerLiteral value = set.elements[0];
+    expect(value.value, 3);
+
+    SpreadElement element = set.elements[1];
+    expect(element.spreadOperator.lexeme, '...');
+    ListLiteral2 spreadExpression = element.expression;
+    expect(spreadExpression.elements, hasLength(1));
+  }
+
+  void test_setLiteral_spread2Q() {
+    SetLiteral2 set = parseCollectionLiteral('{3, ...?[4]}');
+    expect(set.constKeyword, isNull);
+    expect(set.typeArguments, isNull);
+    expect(set.elements, hasLength(2));
+    IntegerLiteral value = set.elements[0];
+    expect(value.value, 3);
+
+    SpreadElement element = set.elements[1];
+    expect(element.spreadOperator.lexeme, '...?');
+    ListLiteral2 spreadExpression = element.expression;
+    expect(spreadExpression.elements, hasLength(1));
+  }
+
+  void test_setLiteral_spread_typed() {
+    SetLiteral2 set = parseCollectionLiteral('<int>{...[3]}');
+    expect(set.constKeyword, isNull);
+    expect(set.typeArguments, isNotNull);
+    expect(set.elements, hasLength(1));
+
+    SpreadElement element = set.elements[0];
+    expect(element.spreadOperator.lexeme, '...');
+    ListLiteral2 spreadExpression = element.expression;
+    expect(spreadExpression.elements, hasLength(1));
+  }
+
+  void test_setLiteral_spreadQ_typed() {
+    SetLiteral2 set = parseCollectionLiteral('<int>{...?[3]}');
+    expect(set.constKeyword, isNull);
+    expect(set.typeArguments, isNotNull);
+    expect(set.elements, hasLength(1));
+
+    SpreadElement element = set.elements[0];
+    expect(element.spreadOperator.lexeme, '...?');
+    ListLiteral2 spreadExpression = element.expression;
+    expect(spreadExpression.elements, hasLength(1));
+  }
+
+  void test_setOrMapLiteral_spread() {
+    MapLiteral2 map = parseCollectionLiteral('{...{3: 4}}');
+    expect(map.constKeyword, isNull);
+    expect(map.typeArguments, isNull);
+    expect(map.entries, hasLength(1));
+
+    SpreadElement element = map.entries[0];
+    expect(element.spreadOperator.lexeme, '...');
+    MapLiteral2 spreadExpression = element.expression;
+    expect(spreadExpression.entries, hasLength(1));
+  }
+
+  void test_setOrMapLiteral_spreadQ() {
+    MapLiteral2 map = parseCollectionLiteral('{...?{3: 4}}');
+    expect(map.constKeyword, isNull);
+    expect(map.typeArguments, isNull);
+    expect(map.entries, hasLength(1));
+
+    SpreadElement element = map.entries[0];
+    expect(element.spreadOperator.lexeme, '...?');
+    MapLiteral2 spreadExpression = element.expression;
+    expect(spreadExpression.entries, hasLength(1));
+  }
+}
+
+/**
  * Tests of the fasta parser based on [ComplexParserTestMixin].
  */
 @reflectiveTest
@@ -261,7 +452,7 @@
   }
 
   void test_listLiteral_spread() {
-    // TODO(danrubel): Revise this test once AST supports new syntax
+    // TODO(danrubel): Remove this once spread_collections is enabled by default
     ListLiteral list = parseExpression('[1, ...[2]]', errors: [
       expectedError(ParserErrorCode.UNEXPECTED_TOKEN, 4, 3),
     ]);
@@ -273,7 +464,7 @@
   }
 
   void test_listLiteral_spreadQ() {
-    // TODO(danrubel): Revise this test once AST supports new syntax
+    // TODO(danrubel): Remove this once spread_collections is enabled by default
     ListLiteral list = parseExpression('[1, ...?[2]]', errors: [
       expectedError(ParserErrorCode.UNEXPECTED_TOKEN, 4, 4),
     ]);
@@ -352,7 +543,7 @@
   }
 
   void test_mapLiteral_spread() {
-    // TODO(danrubel): Revise this once AST supports new syntax
+    // TODO(danrubel): Remove this once spread_collections is enabled by default
     MapLiteral map = parseExpression('{1: 2, ...{3: 4}}',
         parseSetLiterals: true,
         errors: [expectedError(ParserErrorCode.UNEXPECTED_TOKEN, 7, 3)]);
@@ -362,7 +553,7 @@
   }
 
   void test_mapLiteral_spreadQ() {
-    // TODO(danrubel): Revise this once AST supports new syntax
+    // TODO(danrubel): Remove this once spread_collections is enabled by default
     MapLiteral map = parseExpression('{1: 2, ...?{3: 4}}',
         parseSetLiterals: true,
         errors: [expectedError(ParserErrorCode.UNEXPECTED_TOKEN, 7, 4)]);
@@ -372,7 +563,7 @@
   }
 
   void test_mapLiteral_spread_typed() {
-    // TODO(danrubel): Revise this once AST supports new syntax
+    // TODO(danrubel): Remove this once spread_collections is enabled by default
     MapLiteral map = parseExpression('<int, int>{...{3: 4}}',
         parseSetLiterals: true,
         errors: [expectedError(ParserErrorCode.UNEXPECTED_TOKEN, 11, 3)]);
@@ -382,7 +573,7 @@
   }
 
   void test_mapLiteral_spreadQ_typed() {
-    // TODO(danrubel): Revise this once AST supports new syntax
+    // TODO(danrubel): Remove this once spread_collections is enabled by default
     MapLiteral map = parseExpression('<int, int>{...?{3: 4}}',
         parseSetLiterals: true,
         errors: [expectedError(ParserErrorCode.UNEXPECTED_TOKEN, 11, 4)]);
@@ -392,7 +583,7 @@
   }
 
   void test_mapLiteral_spread2_typed() {
-    // TODO(danrubel): Revise this once AST supports new syntax
+    // TODO(danrubel): Remove this once spread_collections is enabled by default
     MapLiteral map = parseExpression('<int, int>{1: 2, ...{3: 4}}',
         parseSetLiterals: true,
         errors: [expectedError(ParserErrorCode.UNEXPECTED_TOKEN, 17, 3)]);
@@ -402,7 +593,7 @@
   }
 
   void test_mapLiteral_spreadQ2_typed() {
-    // TODO(danrubel): Revise this once AST supports new syntax
+    // TODO(danrubel): Remove this once spread_collections is enabled by default
     MapLiteral map = parseExpression('<int, int>{1: 2, ...?{3: 4}}',
         parseSetLiterals: true,
         errors: [expectedError(ParserErrorCode.UNEXPECTED_TOKEN, 17, 4)]);
@@ -471,7 +662,7 @@
   }
 
   void test_setLiteral_spread2() {
-    // TODO(danrubel): Revise this once AST supports new syntax
+    // TODO(danrubel): Remove this once spread_collections is enabled by default
     SetLiteral set = parseExpression('{3, ...[4]}',
         parseSetLiterals: true,
         errors: [expectedError(ParserErrorCode.UNEXPECTED_TOKEN, 4, 3)]);
@@ -485,7 +676,7 @@
   }
 
   void test_setLiteral_spread2Q() {
-    // TODO(danrubel): Revise this once AST supports new syntax
+    // TODO(danrubel): Remove this once spread_collections is enabled by default
     SetLiteral set = parseExpression('{3, ...?[4]}',
         parseSetLiterals: true,
         errors: [expectedError(ParserErrorCode.UNEXPECTED_TOKEN, 4, 4)]);
@@ -499,7 +690,7 @@
   }
 
   void test_setLiteral_spread_typed() {
-    // TODO(danrubel): Revise this once AST supports new syntax
+    // TODO(danrubel): Remove this once spread_collections is enabled by default
     SetLiteral set = parseExpression('<int>{...[3]}',
         parseSetLiterals: true,
         errors: [expectedError(ParserErrorCode.UNEXPECTED_TOKEN, 6, 3)]);
@@ -511,7 +702,7 @@
   }
 
   void test_setLiteral_spreadQ_typed() {
-    // TODO(danrubel): Revise this once AST supports new syntax
+    // TODO(danrubel): Remove this once spread_collections is enabled by default
     SetLiteral set = parseExpression('<int>{...?[3]}',
         parseSetLiterals: true,
         errors: [expectedError(ParserErrorCode.UNEXPECTED_TOKEN, 6, 4)]);
@@ -534,7 +725,7 @@
   }
 
   void test_setOrMapLiteral_spread() {
-    // TODO(danrubel): Revise this once AST supports new syntax
+    // TODO(danrubel): Remove this once spread_collections is enabled by default
     MapLiteral set = parseExpression('{...{3: 4}}',
         parseSetLiterals: true,
         errors: [expectedError(ParserErrorCode.UNEXPECTED_TOKEN, 1, 3)]);
@@ -544,7 +735,7 @@
   }
 
   void test_setOrMapLiteral_spreadQ() {
-    // TODO(danrubel): Revise this once AST supports new syntax
+    // TODO(danrubel): Remove this once spread_collections is enabled by default
     MapLiteral set = parseExpression('{...?{3: 4}}',
         parseSetLiterals: true,
         errors: [expectedError(ParserErrorCode.UNEXPECTED_TOKEN, 1, 4)]);
@@ -810,9 +1001,11 @@
       {List<ErrorCode> codes,
       List<ExpectedError> errors,
       int expectedEndOffset,
-      bool parseSetLiterals = false}) {
+      bool parseSetLiterals = false,
+      bool parseSpreadCollections = false}) {
     createParser(source, expectedEndOffset: expectedEndOffset);
     _parserProxy.fastaParser.enableSetLiterals = parseSetLiterals;
+    _parserProxy.astBuilder.enableSpreadCollections = parseSpreadCollections;
     Expression result = _parserProxy.parseExpression2();
     assertErrors(codes: codes, errors: errors);
     return result;