Improve recovery of enum declaration
Change-Id: I8709e76c38d778d97673e11a8d89d424611a292b
Reviewed-on: https://dart-review.googlesource.com/54543
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Dan Rubel <danrubel@google.com>
diff --git a/pkg/analyzer/test/src/fasta/recovery/partial_code/enum_declaration_test.dart b/pkg/analyzer/test/src/fasta/recovery/partial_code/enum_declaration_test.dart
index 8f2bffa..22b63e2 100644
--- a/pkg/analyzer/test/src/fasta/recovery/partial_code/enum_declaration_test.dart
+++ b/pkg/analyzer/test/src/fasta/recovery/partial_code/enum_declaration_test.dart
@@ -40,7 +40,65 @@
],
'enum _s_ {}',
expectedErrorsInValidCode: [ParserErrorCode.EMPTY_ENUM_BODY]),
+ new TestDescriptor(
+ 'leftBrace',
+ 'enum E {',
+ [
+ ParserErrorCode.MISSING_IDENTIFIER,
+ ScannerErrorCode.EXPECTED_TOKEN
+ ],
+ 'enum E {_s_}',
+ failing: [
+ 'eof' /* tested separately below */,
+ 'typedef',
+ 'functionNonVoid',
+ 'getter',
+ 'setter'
+ ]),
+ new TestDescriptor(
+ 'comma',
+ 'enum E {,',
+ [
+ ParserErrorCode.MISSING_IDENTIFIER,
+ ParserErrorCode.MISSING_IDENTIFIER,
+ ScannerErrorCode.EXPECTED_TOKEN
+ ],
+ 'enum E {_s_,_s_}',
+ failing: [
+ 'eof' /* tested separately below */,
+ 'typedef',
+ 'functionNonVoid',
+ 'getter',
+ 'setter'
+ ]),
+ new TestDescriptor('value', 'enum E {a',
+ [ScannerErrorCode.EXPECTED_TOKEN], 'enum E {a}'),
+ new TestDescriptor(
+ 'commaValue',
+ 'enum E {,a',
+ [
+ ParserErrorCode.MISSING_IDENTIFIER,
+ ScannerErrorCode.EXPECTED_TOKEN
+ ],
+ 'enum E {_s_, a}'),
+ new TestDescriptor('commaRightBrace', 'enum E {,}',
+ [ParserErrorCode.MISSING_IDENTIFIER], 'enum E {_s_}'),
+ new TestDescriptor('commaValueRightBrace', 'enum E {, a}',
+ [ParserErrorCode.MISSING_IDENTIFIER], 'enum E {_s_, a}'),
],
PartialCodeTest.declarationSuffixes);
+ buildTests('enum_eof', [
+ new TestDescriptor(
+ 'leftBrace',
+ 'enum E {',
+ [ParserErrorCode.EMPTY_ENUM_BODY, ScannerErrorCode.EXPECTED_TOKEN],
+ 'enum E {}',
+ expectedErrorsInValidCode: [ParserErrorCode.EMPTY_ENUM_BODY]),
+ new TestDescriptor(
+ 'comma',
+ 'enum E {,',
+ [ParserErrorCode.MISSING_IDENTIFIER, ScannerErrorCode.EXPECTED_TOKEN],
+ 'enum E {_s_}'),
+ ], []);
}
}
diff --git a/pkg/front_end/lib/src/fasta/parser/identifier_context.dart b/pkg/front_end/lib/src/fasta/parser/identifier_context.dart
index f8ecd55..ba67f33 100644
--- a/pkg/front_end/lib/src/fasta/parser/identifier_context.dart
+++ b/pkg/front_end/lib/src/fasta/parser/identifier_context.dart
@@ -93,7 +93,7 @@
/// Identifier is an enumerated value name being declared by an enum
/// declaration.
static const enumValueDeclaration =
- const IdentifierContext('enumValueDeclaration', inDeclaration: true);
+ const EnumValueDeclarationIdentifierContext();
/// Identifier is the name being declared by a class declaration or a named
/// mixin application, for example, `Foo` in `class Foo = X with Y;`.
diff --git a/pkg/front_end/lib/src/fasta/parser/identifier_context_impl.dart b/pkg/front_end/lib/src/fasta/parser/identifier_context_impl.dart
index c98e858..37aedf8 100644
--- a/pkg/front_end/lib/src/fasta/parser/identifier_context_impl.dart
+++ b/pkg/front_end/lib/src/fasta/parser/identifier_context_impl.dart
@@ -130,6 +130,34 @@
}
}
+/// See [IdentifierContext.enumValueDeclaration].
+class EnumValueDeclarationIdentifierContext extends IdentifierContext {
+ const EnumValueDeclarationIdentifierContext()
+ : super('enumValueDeclaration', inDeclaration: true);
+
+ @override
+ Token ensureIdentifier(Token token, Parser parser) {
+ Token identifier = token.next;
+ assert(identifier.kind != IDENTIFIER_TOKEN);
+ if (identifier.isIdentifier) {
+ return identifier;
+ }
+
+ // Recovery
+ parser.reportRecoverableErrorWithToken(
+ identifier, fasta.templateExpectedIdentifier);
+ if (looksLikeStartOfNextTopLevelDeclaration(identifier) ||
+ isOneOfOrEof(identifier, const [',', '}'])) {
+ return insertSyntheticIdentifierAfter(token, parser);
+ } else if (!identifier.isKeywordOrIdentifier) {
+ // When in doubt, consume the token to ensure we make progress
+ // but insert a synthetic identifier to satisfy listeners.
+ return insertSyntheticIdentifierAfter(identifier, parser);
+ }
+ return identifier;
+ }
+}
+
/// See [IdentifierContext.expression].
class ExpressionIdentifierContext extends IdentifierContext {
const ExpressionIdentifierContext()
diff --git a/pkg/front_end/lib/src/fasta/parser/parser.dart b/pkg/front_end/lib/src/fasta/parser/parser.dart
index a39a0b3..72adcc5 100644
--- a/pkg/front_end/lib/src/fasta/parser/parser.dart
+++ b/pkg/front_end/lib/src/fasta/parser/parser.dart
@@ -1614,7 +1614,12 @@
break;
} else {
// Recovery
- if (next.isIdentifier) {
+ Token endGroup = leftBrace.endGroup;
+ if (endGroup.isSynthetic) {
+ // The scanner did not place the synthetic '}' correctly.
+ token = rewriter.moveSynthetic(token, endGroup);
+ break;
+ } else if (next.isIdentifier) {
// If the next token is an identifier, assume a missing comma.
// TODO(danrubel): Consider improved recovery for missing `}`
// both here and when the scanner inserts a synthetic `}`
@@ -2008,8 +2013,6 @@
followingValues = [';'];
} else if (context == IdentifierContext.constructorReferenceContinuation) {
followingValues = ['.', ',', '(', ')', '[', ']', '}', ';'];
- } else if (context == IdentifierContext.enumValueDeclaration) {
- followingValues = [',', '}'];
} else if (context == IdentifierContext.formalParameterDeclaration) {
followingValues = [':', '=', ',', '(', ')', '[', ']', '{', '}'];
} else if (context == IdentifierContext.labelDeclaration) {