Add more fasta parser field recovery

Change-Id: I8a63e04c80931d2b2e8b24a547cc845837dee5d8
Reviewed-on: https://dart-review.googlesource.com/30625
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Dan Rubel <danrubel@google.com>
This commit is contained in:
Dan Rubel 2017-12-20 00:38:38 +00:00 committed by commit-bot@chromium.org
parent e6a05106bf
commit 4e8c6e7862
3 changed files with 92 additions and 20 deletions

View file

@ -739,16 +739,20 @@ class KeywordContributorTest extends DartCompletionContributorTest {
test_class_implements2() async {
addTestSource('class A e^ implements foo');
await computeSuggestions();
// TODO (danrubel) refinement: don't suggest implements
assertSuggestKeywords([Keyword.EXTENDS, Keyword.IMPLEMENTS],
assertSuggestKeywords(
request.target.containingNode is ClassDeclaration
? [Keyword.EXTENDS]
: [Keyword.EXTENDS, Keyword.IMPLEMENTS],
relevance: DART_RELEVANCE_HIGH);
}
test_class_implements3() async {
addTestSource('class A e^ implements foo { }');
await computeSuggestions();
// TODO (danrubel) refinement: don't suggest implements
assertSuggestKeywords([Keyword.EXTENDS, Keyword.IMPLEMENTS],
assertSuggestKeywords(
request.target.containingNode is ClassDeclaration
? [Keyword.EXTENDS]
: [Keyword.EXTENDS, Keyword.IMPLEMENTS],
relevance: DART_RELEVANCE_HIGH);
}

View file

@ -10390,6 +10390,59 @@ class C {
expect(field.name.isSynthetic, isTrue);
}
void test_incompleteField_static() {
CompilationUnit unit = parseCompilationUnit(r'''
class C {
static c
}''', codes: [
ParserErrorCode.MISSING_IDENTIFIER,
ParserErrorCode.EXPECTED_TOKEN
]);
NodeList<CompilationUnitMember> declarations = unit.declarations;
expect(declarations, hasLength(1));
CompilationUnitMember unitMember = declarations[0];
EngineTestCase.assertInstanceOf(
(obj) => obj is ClassDeclaration, ClassDeclaration, unitMember);
NodeList<ClassMember> members = (unitMember as ClassDeclaration).members;
expect(members, hasLength(1));
ClassMember classMember = members[0];
EngineTestCase.assertInstanceOf(
(obj) => obj is FieldDeclaration, FieldDeclaration, classMember);
FieldDeclaration declaration = classMember;
expect(declaration.staticKeyword.lexeme, 'static');
VariableDeclarationList fieldList = declaration.fields;
expect(fieldList.keyword, isNull);
NodeList<VariableDeclaration> fields = fieldList.variables;
expect(fields, hasLength(1));
VariableDeclaration field = fields[0];
expect(field.name.isSynthetic, isTrue);
}
void test_incompleteField_static2() {
CompilationUnit unit = parseCompilationUnit(r'''
class C {
static c x
}''', codes: [ParserErrorCode.EXPECTED_TOKEN]);
NodeList<CompilationUnitMember> declarations = unit.declarations;
expect(declarations, hasLength(1));
CompilationUnitMember unitMember = declarations[0];
EngineTestCase.assertInstanceOf(
(obj) => obj is ClassDeclaration, ClassDeclaration, unitMember);
NodeList<ClassMember> members = (unitMember as ClassDeclaration).members;
expect(members, hasLength(1));
ClassMember classMember = members[0];
EngineTestCase.assertInstanceOf(
(obj) => obj is FieldDeclaration, FieldDeclaration, classMember);
FieldDeclaration declaration = classMember;
expect(declaration.staticKeyword.lexeme, 'static');
VariableDeclarationList fieldList = declaration.fields;
expect(fieldList.keyword, isNull);
NodeList<VariableDeclaration> fields = fieldList.variables;
expect(fields, hasLength(1));
VariableDeclaration field = fields[0];
expect(field.name.isSynthetic, isFalse);
}
void test_incompleteField_type() {
CompilationUnit unit = parseCompilationUnit(r'''
class C {

View file

@ -3510,18 +3510,7 @@ class Parser {
Link<Token> identifiers = findMemberName(start);
if (identifiers.isEmpty) {
if ((isValidTypeReference(token) ||
optional('const', token) ||
optional('final', token) ||
optional('var', token)) &&
isPostIdentifierForRecovery(
token.next, IdentifierContext.fieldDeclaration)) {
// Recovery: Looks like a field declaration but missing a field name.
insertSyntheticIdentifier(token, IdentifierContext.fieldDeclaration);
return parseFields(start, const Link<Token>(), token, false);
} else {
return recoverFromInvalidClassMember(start);
}
return recoverFromInvalidClassMember(start);
}
Token afterName = identifiers.head.next;
identifiers = identifiers.tail;
@ -6052,9 +6041,10 @@ class Parser {
/// Recover from finding an invalid class member. The metadata for the member,
/// if any, has already been parsed (and events have already been generated).
/// The member was expected to start with the token after [beforeMember].
Token recoverFromInvalidClassMember(Token beforeMember) {
Token next = beforeMember.next;
/// The member was expected to start with the token after [token].
Token recoverFromInvalidClassMember(Token token) {
Token start = token;
Token next = token.next;
if (optional(';', next)) {
// Report and skip extra semicolons that appear between members.
// TODO(brianwilkerson) Provide a more specific error message.
@ -6064,8 +6054,33 @@ class Parser {
listener.endMember();
return next;
}
// Skip modifiers
while (isModifier(next)) {
token = next;
next = token.next;
}
if (isValidTypeReference(next) || optional('var', next)) {
if (isPostIdentifierForRecovery(
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);
} 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);
}
} 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);
}
return reportUnrecoverableErrorWithToken(
next, fasta.templateExpectedClassMember);
start, fasta.templateExpectedClassMember);
}
/// Report that the nesting depth of the code being parsed is too large for