Improve fasta field recovery

In addition to improving field recovery, this CL
fixes class field recovery to include an endMember event event
and cleans up missing class body recovery.

Change-Id: I53afe5aef55452108803388de9245f7e14f97833
Reviewed-on: https://dart-review.googlesource.com/32820
Commit-Queue: Dan Rubel <danrubel@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
Dan Rubel
2018-01-06 16:06:30 -05:00
committed by commit-bot@chromium.org
parent 897c6257cd
commit ab229b30ef
3 changed files with 67 additions and 19 deletions
+35 -5
View File
@@ -2364,6 +2364,16 @@ abstract class ErrorParserTestMixin implements AbstractParserTestCase {
[expectedError(ParserErrorCode.ABSTRACT_CLASS_MEMBER, 0, 8)]);
}
void test_abstractClassMember_modifierOnly() {
createParser('final');
ClassMember member = parser.parseClassMember('C');
expectNotNullIfNoErrors(member);
listener.assertErrors([
expectedError(ParserErrorCode.MISSING_IDENTIFIER, 5, 0),
expectedError(ParserErrorCode.EXPECTED_TOKEN, 5, 0)
]);
}
void test_abstractClassMember_setter() {
createParser('abstract set m(v);');
ClassMember member = parser.parseClassMember('C');
@@ -10557,11 +10567,15 @@ class C {
}
void test_incompleteField_static() {
// Fasta recovers by considering the 'c' to be the identifier
// rather than the type.
CompilationUnit unit = parseCompilationUnit(r'''
class C {
static c
}''', codes: [
ParserErrorCode.MISSING_IDENTIFIER,
usingFastaParser
? ParserErrorCode.MISSING_CONST_FINAL_VAR_OR_TYPE
: ParserErrorCode.MISSING_IDENTIFIER,
ParserErrorCode.EXPECTED_TOKEN
]);
NodeList<CompilationUnitMember> declarations = unit.declarations;
@@ -10581,7 +10595,11 @@ class C {
NodeList<VariableDeclaration> fields = fieldList.variables;
expect(fields, hasLength(1));
VariableDeclaration field = fields[0];
expect(field.name.isSynthetic, isTrue);
if (usingFastaParser) {
expect(field.name.name, 'c');
} else {
expect(field.name.isSynthetic, isTrue);
}
}
void test_incompleteField_static2() {
@@ -10610,11 +10628,15 @@ class C {
}
void test_incompleteField_type() {
// Fasta recovers by considering the 'A' to be the identifier
// rather than the type.
CompilationUnit unit = parseCompilationUnit(r'''
class C {
A
}''', codes: [
ParserErrorCode.MISSING_IDENTIFIER,
usingFastaParser
? ParserErrorCode.MISSING_CONST_FINAL_VAR_OR_TYPE
: ParserErrorCode.MISSING_IDENTIFIER,
ParserErrorCode.EXPECTED_TOKEN
]);
NodeList<CompilationUnitMember> declarations = unit.declarations;
@@ -10630,11 +10652,19 @@ class C {
VariableDeclarationList fieldList =
(classMember as FieldDeclaration).fields;
TypeName type = fieldList.type;
expect(type.name.name, 'A');
if (usingFastaParser) {
expect(type, isNull);
} else {
expect(type.name.name, 'A');
}
NodeList<VariableDeclaration> fields = fieldList.variables;
expect(fields, hasLength(1));
VariableDeclaration field = fields[0];
expect(field.name.isSynthetic, isTrue);
if (usingFastaParser) {
expect(field.name.name, 'A');
} else {
expect(field.name.isSynthetic, isTrue);
}
}
void test_incompleteField_var() {
+32 -12
View File
@@ -1495,6 +1495,7 @@ class Parser {
if (!optional('{', token.next)) {
// Recovery
token = parseClassHeaderRecovery(start, begin, classKeyword);
ensureBlock(token, fasta.templateExpectedClassBody);
}
token = parseClassBody(token);
listener.endClassDeclaration(begin, token);
@@ -2250,6 +2251,9 @@ class Parser {
if (memberKind == MemberKind.TopLevelField ||
memberKind == MemberKind.NonStaticField ||
memberKind == MemberKind.StaticField) {
// TODO(danrubel): In this situation, analyzer assumes that
// the user has not yet typed the identifier and thus reports
// a missing identifier.
reportRecoverableError(
begin, fasta.messageMissingConstFinalVarOrType);
listener.handleNoType(begin);
@@ -2792,8 +2796,19 @@ class Parser {
Token name = beforeName.next;
if (token != name) {
reportRecoverableErrorWithToken(token, fasta.templateExtraneousModifier);
token = name;
// Recovery
if (token == name.next) {
// If the name has already been parsed as a modifier or type,
// then insert a synthetic name.
beforeName = name;
token = name = insertSyntheticIdentifier(
beforeName, IdentifierContext.fieldDeclaration);
} else {
// Skip extraneous modifiers.
reportRecoverableErrorWithToken(
token, fasta.templateExtraneousModifier);
token = name;
}
}
IdentifierContext context = isTopLevel
@@ -2971,7 +2986,8 @@ class Parser {
return identifiers;
} else if (optional("=", token) ||
optional(";", token) ||
optional(",", token)) {
optional(",", token) ||
optional("}", token)) {
// A field or abstract getter.
identifiers = identifiers.prepend(previous);
return identifiers;
@@ -3441,13 +3457,9 @@ class Parser {
/// ;
/// ```
Token parseClassBody(Token token) {
Token previousToken = token;
Token begin = token = token.next;
assert(optional('{', token));
listener.beginClassBody(token);
if (!optional('{', token)) {
token =
begin = ensureBlock(previousToken, fasta.templateExpectedClassBody);
}
int count = 0;
while (notEofOrValue('}', token.next)) {
token = parseClassMember(token);
@@ -3561,7 +3573,9 @@ class Parser {
isField = true;
}
break;
} else if ((identical(value, '=')) || (identical(value, ','))) {
} else if (identical(value, '=') ||
identical(value, ',') ||
identical(value, '}')) {
isField = true;
break;
} else {
@@ -6028,20 +6042,26 @@ class Parser {
next.next, IdentifierContext.fieldDeclaration)) {
// Looks like a field declaration but missing a field name.
insertSyntheticIdentifier(next, IdentifierContext.fieldDeclaration);
return parseFields(start, const Link<Token>(), next, false);
token = parseFields(start, const Link<Token>(), next, false);
listener.endMember();
return token;
} else if (next.next.isKeywordOrIdentifier &&
isPostIdentifierForRecovery(
next.next.next, IdentifierContext.fieldDeclaration)) {
// Looks like a field declaration but missing a semicolon
// which parseFields will insert.
return parseFields(start, const Link<Token>(), next, false);
token = parseFields(start, const Link<Token>(), next, false);
listener.endMember();
return token;
}
} else if (token != start &&
isPostIdentifierForRecovery(next, IdentifierContext.fieldDeclaration)) {
// If there is at least one modifier, then
// looks like the start of a field but missing field name.
insertSyntheticIdentifier(token, IdentifierContext.fieldDeclaration);
return parseFields(start, const Link<Token>(), token, false);
token = parseFields(start, const Link<Token>(), token, false);
listener.endMember();
return token;
}
return reportUnrecoverableErrorWithToken(
start, fasta.templateExpectedClassMember);
@@ -2925,7 +2925,6 @@ assertion_initializer_test: Crash
bad_constructor_test/05: CompileTimeError
bad_typedef_test/00: Crash # Issue 28214
bug31436_test: RuntimeError
built_in_identifier_type_annotation_test/13: Crash # Issue 28815
built_in_identifier_type_annotation_test/22: MissingCompileTimeError # Error only in strong mode
built_in_identifier_type_annotation_test/30: Crash # Issue 28815
built_in_identifier_type_annotation_test/52: Crash # Issue 28815
@@ -2944,7 +2943,6 @@ built_in_identifier_type_annotation_test/65: Crash # Issue 28815
built_in_identifier_type_annotation_test/66: Crash # Issue 28815
built_in_identifier_type_annotation_test/67: Crash # Issue 28815
built_in_identifier_type_annotation_test/68: Crash # Issue 28815
built_in_identifier_type_annotation_test/81: Crash # Issue 28815
call_function_apply_test: RuntimeError # Issue 23873
canonical_const2_test: RuntimeError, OK # Issue 1533
closure_param_null_to_object_test: RuntimeError