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:
committed by
commit-bot@chromium.org
parent
897c6257cd
commit
ab229b30ef
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user