allow built-in keywords as identifier in for-in loop
Fix https://github.com/dart-lang/sdk/issues/36678
Change-Id: I2af3a21e96c5654b12c199fc71d87f088e9d739d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/99904
Auto-Submit: Dan Rubel <danrubel@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
diff --git a/pkg/analyzer/test/generated/parser_fasta_test.dart b/pkg/analyzer/test/generated/parser_fasta_test.dart
index 90e4ca8..775196e 100644
--- a/pkg/analyzer/test/generated/parser_fasta_test.dart
+++ b/pkg/analyzer/test/generated/parser_fasta_test.dart
@@ -2649,6 +2649,40 @@
expect(forStatement.body, isNotNull);
}
+ void test_parseForStatement_each_finalRequired() {
+ ForStatement forStatement = parseStatement(
+ 'for (final required in list) {}',
+ parseControlFlowCollections: true,
+ );
+ assertNoErrors();
+ expect(forStatement.awaitKeyword, isNull);
+ expect(forStatement.forKeyword, isNotNull);
+ expect(forStatement.leftParenthesis, isNotNull);
+ ForEachPartsWithDeclaration forLoopParts = forStatement.forLoopParts;
+ expect(forLoopParts.loopVariable.identifier.name, 'required');
+ expect(forLoopParts.inKeyword, isNotNull);
+ expect(forLoopParts.iterable, isNotNull);
+ expect(forStatement.rightParenthesis, isNotNull);
+ expect(forStatement.body, isNotNull);
+ }
+
+ void test_parseForStatement_each_finalExternal() {
+ ForStatement forStatement = parseStatement(
+ 'for (final external in list) {}',
+ parseControlFlowCollections: true,
+ );
+ assertNoErrors();
+ expect(forStatement.awaitKeyword, isNull);
+ expect(forStatement.forKeyword, isNotNull);
+ expect(forStatement.leftParenthesis, isNotNull);
+ ForEachPartsWithDeclaration forLoopParts = forStatement.forLoopParts;
+ expect(forLoopParts.loopVariable.identifier.name, 'external');
+ expect(forLoopParts.inKeyword, isNotNull);
+ expect(forLoopParts.iterable, isNotNull);
+ expect(forStatement.rightParenthesis, isNotNull);
+ expect(forStatement.body, isNotNull);
+ }
+
void test_parseForStatement_each_type2() {
ForStatement forStatement = parseStatement(
'for (A element in list) {}',
diff --git a/pkg/front_end/lib/src/fasta/parser/modifier_context.dart b/pkg/front_end/lib/src/fasta/parser/modifier_context.dart
index 605c9e2..0dc8030 100644
--- a/pkg/front_end/lib/src/fasta/parser/modifier_context.dart
+++ b/pkg/front_end/lib/src/fasta/parser/modifier_context.dart
@@ -2,7 +2,7 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-import '../../scanner/token.dart' show Token;
+import '../../scanner/token.dart' show Keyword, Token;
import '../messages.dart' as fasta;
import 'formal_parameter_kind.dart';
import 'member_kind.dart' show MemberKind;
@@ -21,22 +21,11 @@
// external Foo foo();
// but is the identifier in this declaration
// external() => true;
- if (!token.next.type.isKeyword && !token.next.isIdentifier) {
- return false;
- }
- } else if (token.type.isPseudo) {
- // A pseudo keyword can only be a modifier as long as it is
- // followed by another keyword or two identifiers. Otherwise, it is the
- // identifier or the type before the identifier.
- //
- // For example, `required` is a modifier in this declaration:
- // bar({required Foo foo});
- // but is the type in this declaration
- // bar({required foo});
- // and is the identifier in this declaration
- // bar({required});
- if (!token.next.type.isKeyword &&
- (!token.next.isIdentifier || !token.next.next.isIdentifier)) {
+ // and in
+ // for (final external in list) { }
+ Token next = token.next;
+ Keyword keyword = next.keyword;
+ if (keyword == null && !next.isIdentifier || keyword == Keyword.IN) {
return false;
}
}
diff --git a/pkg/front_end/test/token_test.dart b/pkg/front_end/test/token_test.dart
index ddde7df..d733068 100644
--- a/pkg/front_end/test/token_test.dart
+++ b/pkg/front_end/test/token_test.dart
@@ -156,6 +156,14 @@
}
}
+ void test_noPseudoModifiers() {
+ for (Keyword keyword in Keyword.values) {
+ if (keyword.isModifier) {
+ expect(keyword.isPseudo, isFalse, reason: keyword.lexeme);
+ }
+ }
+ }
+
void test_pseudo_keywords() {
var pseudoKeywords = new Set<Keyword>.from([
Keyword.ASYNC,