Improve typedef identifier recovery

... and also address comment in
https://dart-review.googlesource.com/c/sdk/+/53140

Change-Id: I915fe4433102bfcbdac95bb63416587b03b98680
Reviewed-on: https://dart-review.googlesource.com/53300
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Dan Rubel <danrubel@google.com>
This commit is contained in:
danrubel 2018-05-03 15:14:43 +00:00 committed by commit-bot@chromium.org
parent 46a9ed0617
commit e93b2ee250
7 changed files with 101 additions and 13 deletions

View file

@ -4117,11 +4117,11 @@ class Wrong<T> {
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),

View file

@ -77,7 +77,7 @@ class ImportDirectivesTest extends PartialCodeTest {
],
"import 'a.dart' if (b) '';"),
new TestDescriptor(
'ifCondition',
'as',
"import 'a.dart' as",
[
ParserErrorCode.MISSING_IDENTIFIER,

View file

@ -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();
});

View file

@ -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<String> 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);
}
}

View file

@ -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.`).

View file

@ -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()

View file

@ -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())