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;