mirror of
https://github.com/dart-lang/sdk
synced 2024-10-06 16:00:45 +00:00
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:
parent
e6a05106bf
commit
4e8c6e7862
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue