Improve fasta parser import prefix recovery

Change-Id: Ib1265cee27ff4e7cfc20012d3e4281cd151accdc
Reviewed-on: https://dart-review.googlesource.com/53140
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Dan Rubel <danrubel@google.com>
This commit is contained in:
danrubel 2018-05-01 11:26:11 +00:00 committed by commit-bot@chromium.org
parent d85c175a22
commit bf91621857
4 changed files with 59 additions and 8 deletions

View file

@ -76,6 +76,15 @@ class ImportDirectivesTest extends PartialCodeTest {
ParserErrorCode.EXPECTED_STRING_LITERAL
],
"import 'a.dart' if (b) '';"),
new TestDescriptor(
'ifCondition',
"import 'a.dart' as",
[
ParserErrorCode.MISSING_IDENTIFIER,
ParserErrorCode.EXPECTED_TOKEN
],
"import 'a.dart' as _s_;",
failing: ['functionNonVoid', 'getter']),
],
PartialCodeTest.prePartSuffixes);
}

View file

@ -22,10 +22,7 @@ import 'parser.dart' show Parser;
class IdentifierContext {
/// Identifier is being declared as the name of an import prefix (i.e. `Foo`
/// in `import "..." as Foo;`)
static const importPrefixDeclaration = const IdentifierContext(
'importPrefixDeclaration',
inDeclaration: true,
isBuiltInIdentifierAllowed: false);
static const importPrefixDeclaration = const ImportPrefixIdentifierContext();
/// Identifier is the start of a dotted name in a conditional import or
/// export.

View file

@ -31,6 +31,7 @@ class ClassOrNamedMixinIdentifierContext extends IdentifierContext {
return identifier;
}
// Recovery
if (looksLikeStartOfNextTopLevelDeclaration(identifier) ||
isOneOfOrEof(
identifier, const ['<', '{', 'extends', 'with', 'implements'])) {
@ -76,6 +77,7 @@ class DottedNameIdentifierContext extends IdentifierContext {
}
}
// Recovery
if (looksLikeStartOfNextTopLevelDeclaration(identifier) ||
isOneOfOrEof(identifier, followingValues)) {
identifier = parser.insertSyntheticIdentifier(token, this,
@ -123,6 +125,8 @@ class ExpressionIdentifierContext extends IdentifierContext {
}
return identifier;
}
// Recovery
parser.reportRecoverableErrorWithToken(
identifier, fasta.templateExpectedIdentifier);
if (identifier.isKeywordOrIdentifier) {
@ -152,6 +156,7 @@ class FieldDeclarationIdentifierContext extends IdentifierContext {
if (identifier.isIdentifier) {
return identifier;
}
// Recovery
if (isOneOfOrEof(identifier, const [';', '=', ',', '}']) ||
looksLikeStartOfNextClassMember(identifier)) {
@ -183,6 +188,8 @@ class FieldInitializerIdentifierContext extends IdentifierContext {
if (identifier.isIdentifier) {
return identifier;
}
// Recovery
parser.reportRecoverableErrorWithToken(
identifier, fasta.templateExpectedIdentifier);
// Insert a synthetic identifier to satisfy listeners.
@ -190,6 +197,43 @@ class FieldInitializerIdentifierContext extends IdentifierContext {
}
}
/// See [IdentifierContext].importPrefixDeclaration
class ImportPrefixIdentifierContext extends IdentifierContext {
const ImportPrefixIdentifierContext()
: super('importPrefixDeclaration',
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 [';', 'if', 'show', 'hide', 'deferred', 'as'];
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].libraryName
class LibraryIdentifierContext extends IdentifierContext {
const LibraryIdentifierContext()
@ -213,6 +257,8 @@ class LibraryIdentifierContext extends IdentifierContext {
// is invalid and this looks like the start of the next declaration.
// In this situation, fall through to insert a synthetic library name.
}
// Recovery
if (isOneOfOrEof(identifier, const ['.', ';']) ||
looksLikeStartOfNextTopLevelDeclaration(identifier)) {
identifier = parser.insertSyntheticIdentifier(token, this,
@ -243,6 +289,8 @@ class LocalVariableDeclarationIdentifierContext extends IdentifierContext {
checkAsyncAwaitYieldAsIdentifier(identifier, parser);
return identifier;
}
// Recovery
if (isOneOfOrEof(identifier, const [';', '=', ',', '{', '}']) ||
looksLikeStartOfNextStatement(identifier)) {
identifier = parser.insertSyntheticIdentifier(token, this,
@ -275,6 +323,7 @@ class MethodDeclarationIdentifierContext extends IdentifierContext {
if (identifier.isIdentifier) {
return identifier;
}
// Recovery
if (identifier.isUserDefinableOperator && !isContinuation) {
return parser.insertSyntheticIdentifier(identifier, this,

View file

@ -2005,8 +2005,6 @@ class Parser {
followingValues = [',', '}'];
} else if (context == IdentifierContext.formalParameterDeclaration) {
followingValues = [':', '=', ',', '(', ')', '[', ']', '{', '}'];
} else if (context == IdentifierContext.importPrefixDeclaration) {
followingValues = [';', 'hide', 'show', 'deferred', 'as'];
} else if (context == IdentifierContext.labelDeclaration) {
followingValues = [':'];
} else if (context == IdentifierContext.literalSymbol ||
@ -2084,8 +2082,6 @@ class Parser {
..addAll(classMemberKeywords())
..addAll(statementKeywords())
..add('covariant');
} else if (context == IdentifierContext.importPrefixDeclaration) {
initialKeywords = topLevelKeywords();
} else if (context == IdentifierContext.labelDeclaration) {
initialKeywords = statementKeywords();
} else if (context == IdentifierContext.localAccessorDeclaration) {