From ab229b30efeca375049fecb409d68fcda2c2b2fc Mon Sep 17 00:00:00 2001 From: Dan Rubel Date: Sat, 6 Jan 2018 16:06:30 -0500 Subject: [PATCH] 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 Reviewed-by: Brian Wilkerson --- pkg/analyzer/test/generated/parser_test.dart | 40 ++++++++++++++--- .../lib/src/fasta/parser/parser.dart | 44 ++++++++++++++----- tests/language_2/language_2_dart2js.status | 2 - 3 files changed, 67 insertions(+), 19 deletions(-) diff --git a/pkg/analyzer/test/generated/parser_test.dart b/pkg/analyzer/test/generated/parser_test.dart index 1fa6f9ace41..3aa5c66e1e5 100644 --- a/pkg/analyzer/test/generated/parser_test.dart +++ b/pkg/analyzer/test/generated/parser_test.dart @@ -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 declarations = unit.declarations; @@ -10581,7 +10595,11 @@ class C { NodeList 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 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 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() { diff --git a/pkg/front_end/lib/src/fasta/parser/parser.dart b/pkg/front_end/lib/src/fasta/parser/parser.dart index 24ff091c871..3d4ac471d3c 100644 --- a/pkg/front_end/lib/src/fasta/parser/parser.dart +++ b/pkg/front_end/lib/src/fasta/parser/parser.dart @@ -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(), next, false); + token = parseFields(start, const Link(), 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(), next, false); + token = parseFields(start, const Link(), 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, false); + token = parseFields(start, const Link(), token, false); + listener.endMember(); + return token; } return reportUnrecoverableErrorWithToken( start, fasta.templateExpectedClassMember); diff --git a/tests/language_2/language_2_dart2js.status b/tests/language_2/language_2_dart2js.status index 34c5406d2a4..0a716b032a7 100644 --- a/tests/language_2/language_2_dart2js.status +++ b/tests/language_2/language_2_dart2js.status @@ -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