diff --git a/pkg/analyzer/test/generated/parser_test.dart b/pkg/analyzer/test/generated/parser_test.dart index 83855d0c508..5d569496757 100644 --- a/pkg/analyzer/test/generated/parser_test.dart +++ b/pkg/analyzer/test/generated/parser_test.dart @@ -4117,11 +4117,11 @@ class Wrong { parseCompilationUnit("typedef var Function(var arg);", errors: usingFastaParser ? [ + expectedError(ParserErrorCode.EXPECTED_TYPE_NAME, 8, 3), + expectedError(ParserErrorCode.VAR_AND_TYPE, 21, 3), + expectedError(ParserErrorCode.MISSING_IDENTIFIER, 29, 1), expectedError( - ParserErrorCode.MISSING_CONST_FINAL_VAR_OR_TYPE, 0, 7), - expectedError(ParserErrorCode.EXPECTED_TOKEN, 8, 3), - expectedError(ParserErrorCode.VAR_RETURN_TYPE, 8, 3), - expectedError(ParserErrorCode.MISSING_FUNCTION_BODY, 29, 1), + ParserErrorCode.MISSING_TYPEDEF_PARAMETERS, 29, 1), ] : [ expectedError(ParserErrorCode.MISSING_IDENTIFIER, 8, 3), diff --git a/pkg/analyzer/test/src/fasta/recovery/partial_code/import_directive_test.dart b/pkg/analyzer/test/src/fasta/recovery/partial_code/import_directive_test.dart index e04d1b451f0..dd46a45e864 100644 --- a/pkg/analyzer/test/src/fasta/recovery/partial_code/import_directive_test.dart +++ b/pkg/analyzer/test/src/fasta/recovery/partial_code/import_directive_test.dart @@ -77,7 +77,7 @@ class ImportDirectivesTest extends PartialCodeTest { ], "import 'a.dart' if (b) '';"), new TestDescriptor( - 'ifCondition', + 'as', "import 'a.dart' as", [ ParserErrorCode.MISSING_IDENTIFIER, diff --git a/pkg/analyzer/test/src/fasta/recovery/partial_code/test_all.dart b/pkg/analyzer/test/src/fasta/recovery/partial_code/test_all.dart index 735402be4dc..eb14122c322 100644 --- a/pkg/analyzer/test/src/fasta/recovery/partial_code/test_all.dart +++ b/pkg/analyzer/test/src/fasta/recovery/partial_code/test_all.dart @@ -26,6 +26,7 @@ import 'return_statement_test.dart' as return_statement; import 'switch_statement_test.dart' as switch_statement; import 'top_level_variable_test.dart' as top_level_variable; import 'try_statement_test.dart' as try_statement; +import 'typedef_test.dart' as typedef_declaration; import 'while_statement_test.dart' as while_statement; import 'yield_statement_test.dart' as yield_statement; @@ -53,6 +54,7 @@ main() { switch_statement.main(); top_level_variable.main(); try_statement.main(); + typedef_declaration.main(); while_statement.main(); yield_statement.main(); }); diff --git a/pkg/analyzer/test/src/fasta/recovery/partial_code/typedef_test.dart b/pkg/analyzer/test/src/fasta/recovery/partial_code/typedef_test.dart new file mode 100644 index 00000000000..9d918153d37 --- /dev/null +++ b/pkg/analyzer/test/src/fasta/recovery/partial_code/typedef_test.dart @@ -0,0 +1,50 @@ +// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:analyzer/src/dart/error/syntactic_errors.dart'; + +import 'partial_code_support.dart'; + +main() { + new TypedefTest().buildAll(); +} + +class TypedefTest extends PartialCodeTest { + buildAll() { + List allExceptEof = + PartialCodeTest.declarationSuffixes.map((t) => t.name).toList(); + buildTests( + 'typedef', + [ + new TestDescriptor( + 'keyword', + 'typedef', + [ + ParserErrorCode.MISSING_IDENTIFIER, + ParserErrorCode.MISSING_TYPEDEF_PARAMETERS, + ParserErrorCode.EXPECTED_TOKEN + ], + "typedef _s_();", + failing: [ + 'functionVoid', + 'functionNonVoid', + 'var', + 'const', + 'final', + 'getter' + ]), + new TestDescriptor( + 'keywordEquals', + 'typedef =', + [ + ParserErrorCode.MISSING_IDENTIFIER, + ParserErrorCode.EXPECTED_TYPE_NAME, + ParserErrorCode.EXPECTED_TOKEN + ], + "typedef _s_ = _s_;", + failing: allExceptEof), + ], + PartialCodeTest.declarationSuffixes); + } +} diff --git a/pkg/front_end/lib/src/fasta/parser/identifier_context.dart b/pkg/front_end/lib/src/fasta/parser/identifier_context.dart index 0199b13fb96..9d57e58cb8d 100644 --- a/pkg/front_end/lib/src/fasta/parser/identifier_context.dart +++ b/pkg/front_end/lib/src/fasta/parser/identifier_context.dart @@ -54,10 +54,7 @@ class IdentifierContext { isContinuation: true); /// Identifier is the name being declared by a typedef declaration. - static const typedefDeclaration = const IdentifierContext( - 'typedefDeclaration', - inDeclaration: true, - isBuiltInIdentifierAllowed: false); + static const typedefDeclaration = const TypedefDeclarationIdentifierContext(); /// Identifier is a field initializer in a formal parameter list (i.e. it /// appears directly after `this.`). diff --git a/pkg/front_end/lib/src/fasta/parser/identifier_context_impl.dart b/pkg/front_end/lib/src/fasta/parser/identifier_context_impl.dart index 000b2f23d56..ffae9762fdb 100644 --- a/pkg/front_end/lib/src/fasta/parser/identifier_context_impl.dart +++ b/pkg/front_end/lib/src/fasta/parser/identifier_context_impl.dart @@ -346,6 +346,43 @@ class MethodDeclarationIdentifierContext extends IdentifierContext { } } +/// See [IdentifierContext].typedefDeclaration +class TypedefDeclarationIdentifierContext extends IdentifierContext { + const TypedefDeclarationIdentifierContext() + : super('typedefDeclaration', + inDeclaration: true, isBuiltInIdentifierAllowed: false); + + @override + Token ensureIdentifier(Token token, Parser parser) { + Token identifier = token.next; + assert(identifier.kind != IDENTIFIER_TOKEN); + if (identifier.type.isPseudo) { + return identifier; + } + + // Recovery + const followingValues = const ['(', '<', '=', ';', 'var']; + if (identifier.type.isBuiltIn && + isOneOfOrEof(identifier.next, followingValues)) { + parser.reportRecoverableErrorWithToken( + identifier, fasta.templateBuiltInIdentifierInDeclaration); + } else if (looksLikeStartOfNextTopLevelDeclaration(identifier) || + isOneOfOrEof(identifier, followingValues)) { + identifier = parser.insertSyntheticIdentifier(token, this, + message: fasta.templateExpectedIdentifier.withArguments(identifier)); + } else { + parser.reportRecoverableErrorWithToken( + identifier, fasta.templateExpectedIdentifier); + if (!identifier.isKeywordOrIdentifier) { + // When in doubt, consume the token to ensure we make progress + // but insert a synthetic identifier to satisfy listeners. + identifier = insertSyntheticIdentifierAfter(identifier, parser); + } + } + return identifier; + } +} + /// See [IdentifierContext].typeReference class TypeReferenceIdentifierContext extends IdentifierContext { const TypeReferenceIdentifierContext() diff --git a/pkg/front_end/lib/src/fasta/parser/parser.dart b/pkg/front_end/lib/src/fasta/parser/parser.dart index d58fa45a19b..e74907f9db4 100644 --- a/pkg/front_end/lib/src/fasta/parser/parser.dart +++ b/pkg/front_end/lib/src/fasta/parser/parser.dart @@ -534,6 +534,12 @@ class Parser { directiveState?.checkDeclaration(); if (next.isIdentifier || optional("void", next)) { return parseTypedef(previous); + } else if (next.isTopLevelKeyword || + optional('var', next) || + optional('=', next) || + next.isEof) { + // Recovery + return parseTypedef(previous); } else { return parseTopLevelMemberImpl(previous); } @@ -2019,8 +2025,6 @@ class Parser { followingValues = ['(', '{', '=>']; } else if (context == IdentifierContext.topLevelVariableDeclaration) { followingValues = [';', '=', ',']; - } else if (context == IdentifierContext.typedefDeclaration) { - followingValues = ['(', '<', ';']; } else if (context == IdentifierContext.typeVariableDeclaration) { followingValues = ['<', '>', ';', '}']; } else { @@ -2095,8 +2099,6 @@ class Parser { initialKeywords = topLevelKeywords(); } else if (context == IdentifierContext.topLevelVariableDeclaration) { initialKeywords = topLevelKeywords(); - } else if (context == IdentifierContext.typedefDeclaration) { - initialKeywords = topLevelKeywords(); } else if (context == IdentifierContext.typeVariableDeclaration) { initialKeywords = topLevelKeywords() ..addAll(classMemberKeywords())