From 2faab1d4c0ddb2629cc3b8cbfa7f0f2b414d09df Mon Sep 17 00:00:00 2001 From: danrubel Date: Fri, 18 Jan 2019 00:05:30 +0000 Subject: [PATCH] Update AstBuilder to build spread collection AST structures Change-Id: Ifde5047933d810bdda77fe1b46eb6f69b7eabc5a Reviewed-on: https://dart-review.googlesource.com/c/90082 Reviewed-by: Brian Wilkerson Commit-Queue: Dan Rubel --- pkg/analyzer/lib/dart/ast/ast.dart | 2 +- pkg/analyzer/lib/src/fasta/ast_builder.dart | 89 +++++-- .../test/generated/parser_fasta_test.dart | 223 ++++++++++++++++-- 3 files changed, 275 insertions(+), 39 deletions(-) diff --git a/pkg/analyzer/lib/dart/ast/ast.dart b/pkg/analyzer/lib/dart/ast/ast.dart index 5fb2d22128b..fdb49c84119 100644 --- a/pkg/analyzer/lib/dart/ast/ast.dart +++ b/pkg/analyzer/lib/dart/ast/ast.dart @@ -5142,7 +5142,7 @@ abstract class ListLiteral extends TypedLiteral { * * 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 477b9c1a3f9..7c1350eb0f1 100644 --- a/pkg/analyzer/lib/src/fasta/ast_builder.dart +++ b/pkg/analyzer/lib/src/fasta/ast_builder.dart @@ -279,9 +279,12 @@ class AstBuilder extends StackListener { @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 @@ class AstBuilder extends StackListener { assert(optional(']', rightBracket)); debugEvent("LiteralList"); - List expressions = popTypedList(count); - TypeArgumentList typeArguments = pop(); - push(ast.listLiteral( - constKeyword, typeArguments, leftBracket, expressions, rightBracket)); + if (enableSpreadCollections) { + List elements = popTypedList(count); + TypeArgumentList typeArguments = pop(); + push(ast.listLiteral2( + constKeyword: constKeyword, + typeArguments: typeArguments, + leftBracket: leftBracket, + elements: elements, + rightBracket: rightBracket, + )); + } else { + List expressions = popTypedList(count); + TypeArgumentList typeArguments = pop(); + push(ast.listLiteral( + constKeyword, typeArguments, leftBracket, expressions, rightBracket)); + } } void handleAsyncModifier(Token asyncToken, Token starToken) { @@ -927,10 +942,23 @@ class AstBuilder extends StackListener { assert(optional('}', rightBracket)); debugEvent("LiteralSet"); - List entries = popTypedList(count) ?? []; - TypeArgumentList typeArguments = pop(); - push(ast.setLiteral( - constKeyword, typeArguments, leftBracket, entries, rightBracket)); + if (enableSpreadCollections) { + List elements = + popTypedList(count) ?? []; + TypeArgumentList typeArguments = pop(); + push(ast.setLiteral2( + constKeyword: constKeyword, + typeArguments: typeArguments, + leftBracket: leftBracket, + elements: elements, + rightBracket: rightBracket, + )); + } else { + List entries = popTypedList(count) ?? []; + TypeArgumentList typeArguments = pop(); + push(ast.setLiteral( + constKeyword, typeArguments, leftBracket, entries, rightBracket)); + } } void handleLiteralMap( @@ -940,19 +968,34 @@ class AstBuilder extends StackListener { 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 entries = []; - popTypedList(count)?.forEach((entry) { - if (entry is MapLiteralEntry) { - entries.add(entry); - } - }); + if (enableSpreadCollections) { + List entries = []; + 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 entries = []; + 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 6b798e183f1..779173acf07 100644 --- a/pkg/analyzer/test/generated/parser_fasta_test.dart +++ b/pkg/analyzer/test/generated/parser_fasta_test.dart @@ -40,6 +40,7 @@ main() { defineReflectiveTests(NNBDParserTest_Fasta); defineReflectiveTests(RecoveryParserTest_Fasta); defineReflectiveTests(SimpleParserTest_Fasta); + defineReflectiveTests(CollectionLiteralParserTest); defineReflectiveTests(StatementParserTest_Fasta); defineReflectiveTests(TopLevelParserTest_Fasta); }); @@ -104,6 +105,196 @@ class ClassMemberParserTest_Fasta extends FastaParserTestCase } } +/** + * Tests of the fasta parser based on [ExpressionParserTestMixin]. + */ +@reflectiveTest +class CollectionLiteralParserTest extends FastaParserTestCase { + Expression parseCollectionLiteral(String source, + {List codes, + List 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('{...{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('{...?{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('{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('{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('{...[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('{...?[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]. */ @@ -261,7 +452,7 @@ class ExpressionParserTest_Fasta extends FastaParserTestCase } 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 @@ class ExpressionParserTest_Fasta extends FastaParserTestCase } 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 @@ class ExpressionParserTest_Fasta extends FastaParserTestCase } 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 @@ class ExpressionParserTest_Fasta extends FastaParserTestCase } 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 @@ class ExpressionParserTest_Fasta extends FastaParserTestCase } 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('{...{3: 4}}', parseSetLiterals: true, errors: [expectedError(ParserErrorCode.UNEXPECTED_TOKEN, 11, 3)]); @@ -382,7 +573,7 @@ class ExpressionParserTest_Fasta extends FastaParserTestCase } 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('{...?{3: 4}}', parseSetLiterals: true, errors: [expectedError(ParserErrorCode.UNEXPECTED_TOKEN, 11, 4)]); @@ -392,7 +583,7 @@ class ExpressionParserTest_Fasta extends FastaParserTestCase } 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('{1: 2, ...{3: 4}}', parseSetLiterals: true, errors: [expectedError(ParserErrorCode.UNEXPECTED_TOKEN, 17, 3)]); @@ -402,7 +593,7 @@ class ExpressionParserTest_Fasta extends FastaParserTestCase } 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('{1: 2, ...?{3: 4}}', parseSetLiterals: true, errors: [expectedError(ParserErrorCode.UNEXPECTED_TOKEN, 17, 4)]); @@ -471,7 +662,7 @@ class ExpressionParserTest_Fasta extends FastaParserTestCase } 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 @@ class ExpressionParserTest_Fasta extends FastaParserTestCase } 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 @@ class ExpressionParserTest_Fasta extends FastaParserTestCase } 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('{...[3]}', parseSetLiterals: true, errors: [expectedError(ParserErrorCode.UNEXPECTED_TOKEN, 6, 3)]); @@ -511,7 +702,7 @@ class ExpressionParserTest_Fasta extends FastaParserTestCase } 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('{...?[3]}', parseSetLiterals: true, errors: [expectedError(ParserErrorCode.UNEXPECTED_TOKEN, 6, 4)]); @@ -534,7 +725,7 @@ class ExpressionParserTest_Fasta extends FastaParserTestCase } 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 @@ class ExpressionParserTest_Fasta extends FastaParserTestCase } 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 @@ class FastaParserTestCase {List codes, List 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;