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.';
}