mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 12:30:03 +00:00
Improve import/export combinator identifier recovery
Change-Id: I9f0a418d3b1fae001199c27850b1a85daf2d07a6 Reviewed-on: https://dart-review.googlesource.com/56040 Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Commit-Queue: Dan Rubel <danrubel@google.com>
This commit is contained in:
parent
79a0ef60e0
commit
1cf871a4d2
|
@ -1158,7 +1158,9 @@ class ForwardingTestListener extends ForwardingListener {
|
|||
|
||||
@override
|
||||
void handleImportPrefix(Token deferredKeyword, Token asKeyword) {
|
||||
expectIn('Import');
|
||||
// This event normally happens within "Import",
|
||||
// but happens within "CompilationUnit" during recovery.
|
||||
expectInOneOf(const ['Import', 'CompilationUnit']);
|
||||
listener.handleImportPrefix(deferredKeyword, asKeyword);
|
||||
}
|
||||
|
||||
|
|
|
@ -4016,6 +4016,11 @@ class RecoveryParserTest_Forest extends FastaBodyBuilderTestCase
|
|||
super.test_classTypeAlias_withBody();
|
||||
}
|
||||
|
||||
@failingTest
|
||||
void test_combinator_badIdentifier() {
|
||||
super.test_combinator_badIdentifier();
|
||||
}
|
||||
|
||||
@failingTest
|
||||
void test_combinator_missingIdentifier() {
|
||||
super.test_combinator_missingIdentifier();
|
||||
|
|
|
@ -10787,6 +10787,25 @@ class B = Object with A {}''',
|
|||
: [ParserErrorCode.EXPECTED_TOKEN]);
|
||||
}
|
||||
|
||||
void test_combinator_badIdentifier() {
|
||||
createParser('import "/testB.dart" show @');
|
||||
parser.parseCompilationUnit2();
|
||||
listener.assertErrors(usingFastaParser
|
||||
? [
|
||||
expectedError(ParserErrorCode.MISSING_IDENTIFIER, 26, 1),
|
||||
expectedError(ParserErrorCode.EXPECTED_TOKEN, 27, 0),
|
||||
expectedError(
|
||||
ParserErrorCode.MISSING_CONST_FINAL_VAR_OR_TYPE, 27, 0),
|
||||
expectedError(ParserErrorCode.EXPECTED_TOKEN, 27, 0)
|
||||
]
|
||||
: [
|
||||
expectedError(ParserErrorCode.MISSING_IDENTIFIER, 26, 1),
|
||||
expectedError(ParserErrorCode.EXPECTED_TOKEN, 26, 1),
|
||||
expectedError(ParserErrorCode.MISSING_IDENTIFIER, 26, 1),
|
||||
expectedError(ParserErrorCode.EXPECTED_EXECUTABLE, 27, 1)
|
||||
]);
|
||||
}
|
||||
|
||||
void test_combinator_missingIdentifier() {
|
||||
createParser('import "/testB.dart" show ;');
|
||||
parser.parseCompilationUnit2();
|
||||
|
|
|
@ -12,20 +12,6 @@ main() {
|
|||
|
||||
class ExportDirectivesTest extends PartialCodeTest {
|
||||
buildAll() {
|
||||
List<String> allExceptEof = <String>[
|
||||
'import',
|
||||
'export',
|
||||
'part',
|
||||
'class',
|
||||
'typedef',
|
||||
'functionVoid',
|
||||
'functionNonVoid',
|
||||
'var',
|
||||
'const',
|
||||
'final',
|
||||
'getter',
|
||||
'setter'
|
||||
];
|
||||
buildTests(
|
||||
'export_directive',
|
||||
[
|
||||
|
@ -51,7 +37,7 @@ class ExportDirectivesTest extends PartialCodeTest {
|
|||
ParserErrorCode.EXPECTED_TOKEN
|
||||
],
|
||||
"export 'a.dart' hide _s_;",
|
||||
failing: allExceptEof),
|
||||
failing: ['functionNonVoid', 'getter']),
|
||||
new TestDescriptor('hideName', "export 'a.dart' hide A",
|
||||
[ParserErrorCode.EXPECTED_TOKEN], "export 'a.dart' hide A;"),
|
||||
new TestDescriptor(
|
||||
|
@ -62,7 +48,7 @@ class ExportDirectivesTest extends PartialCodeTest {
|
|||
ParserErrorCode.EXPECTED_TOKEN
|
||||
],
|
||||
"export 'a.dart' hide A, _s_;",
|
||||
failing: allExceptEof),
|
||||
failing: ['functionNonVoid', 'getter']),
|
||||
new TestDescriptor('hideCommaName', "export 'a.dart' hide A, B",
|
||||
[ParserErrorCode.EXPECTED_TOKEN], "export 'a.dart' hide A, B;"),
|
||||
new TestDescriptor(
|
||||
|
@ -73,7 +59,7 @@ class ExportDirectivesTest extends PartialCodeTest {
|
|||
ParserErrorCode.EXPECTED_TOKEN
|
||||
],
|
||||
"export 'a.dart' hide A show _s_;",
|
||||
failing: allExceptEof),
|
||||
failing: ['functionNonVoid', 'getter']),
|
||||
new TestDescriptor(
|
||||
'show',
|
||||
"export 'a.dart' show",
|
||||
|
@ -82,7 +68,7 @@ class ExportDirectivesTest extends PartialCodeTest {
|
|||
ParserErrorCode.EXPECTED_TOKEN
|
||||
],
|
||||
"export 'a.dart' show _s_;",
|
||||
failing: allExceptEof),
|
||||
failing: ['functionNonVoid', 'getter']),
|
||||
new TestDescriptor('showName', "export 'a.dart' show A",
|
||||
[ParserErrorCode.EXPECTED_TOKEN], "export 'a.dart' show A;"),
|
||||
new TestDescriptor(
|
||||
|
@ -93,7 +79,7 @@ class ExportDirectivesTest extends PartialCodeTest {
|
|||
ParserErrorCode.EXPECTED_TOKEN
|
||||
],
|
||||
"export 'a.dart' show A, _s_;",
|
||||
failing: allExceptEof),
|
||||
failing: ['functionNonVoid', 'getter']),
|
||||
new TestDescriptor('showCommaName', "export 'a.dart' show A, B",
|
||||
[ParserErrorCode.EXPECTED_TOKEN], "export 'a.dart' show A, B;"),
|
||||
new TestDescriptor(
|
||||
|
@ -104,7 +90,7 @@ class ExportDirectivesTest extends PartialCodeTest {
|
|||
ParserErrorCode.EXPECTED_TOKEN
|
||||
],
|
||||
"export 'a.dart' show A hide _s_;",
|
||||
failing: allExceptEof),
|
||||
failing: ['functionNonVoid', 'getter']),
|
||||
],
|
||||
PartialCodeTest.prePartSuffixes);
|
||||
}
|
||||
|
|
|
@ -85,6 +85,15 @@ class ImportDirectivesTest extends PartialCodeTest {
|
|||
],
|
||||
"import 'a.dart' as _s_;",
|
||||
failing: ['functionNonVoid', 'getter']),
|
||||
new TestDescriptor(
|
||||
'show',
|
||||
"import 'a.dart' show",
|
||||
[
|
||||
ParserErrorCode.EXPECTED_TOKEN,
|
||||
ParserErrorCode.MISSING_IDENTIFIER
|
||||
],
|
||||
"import 'a.dart' show _s_;",
|
||||
failing: ['functionNonVoid', 'getter']),
|
||||
],
|
||||
PartialCodeTest.prePartSuffixes);
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ class IdentifierContext {
|
|||
|
||||
/// Identifier is one of the shown/hidden names in an import/export
|
||||
/// combinator.
|
||||
static const combinator = const IdentifierContext('combinator');
|
||||
static const combinator = const CombinatorIdentifierContext();
|
||||
|
||||
/// Identifier is the start of a name in an annotation that precedes a
|
||||
/// declaration (i.e. it appears directly after an `@`).
|
||||
|
|
|
@ -52,6 +52,44 @@ class ClassOrNamedMixinIdentifierContext extends IdentifierContext {
|
|||
}
|
||||
}
|
||||
|
||||
/// See [IdentifierContext.combinator].
|
||||
class CombinatorIdentifierContext extends IdentifierContext {
|
||||
const CombinatorIdentifierContext() : super('combinator');
|
||||
|
||||
@override
|
||||
Token ensureIdentifier(Token token, Parser parser) {
|
||||
Token identifier = token.next;
|
||||
assert(identifier.kind != IDENTIFIER_TOKEN);
|
||||
const followingValues = const [';', ',', 'if', 'as', 'show', 'hide'];
|
||||
|
||||
if (identifier.isIdentifier) {
|
||||
if (!looksLikeStartOfNextTopLevelDeclaration(identifier) ||
|
||||
isOneOfOrEof(identifier.next, followingValues)) {
|
||||
return identifier;
|
||||
}
|
||||
// Although this is a valid identifier name, the import declaration
|
||||
// is invalid and this looks like the start of the next declaration.
|
||||
// In this situation, fall through to insert a synthetic identifier.
|
||||
}
|
||||
|
||||
// Recovery
|
||||
if (isOneOfOrEof(identifier, followingValues) ||
|
||||
looksLikeStartOfNextTopLevelDeclaration(identifier)) {
|
||||
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 = parser.rewriter.insertSyntheticIdentifier(identifier);
|
||||
}
|
||||
}
|
||||
return identifier;
|
||||
}
|
||||
}
|
||||
|
||||
/// See [IdentifierContext.dottedName].
|
||||
class DottedNameIdentifierContext extends IdentifierContext {
|
||||
const DottedNameIdentifierContext() : super('dottedName');
|
||||
|
@ -655,5 +693,4 @@ bool looksLikeStartOfNextStatement(Token token) => isOneOfOrEof(token, const [
|
|||
|
||||
bool looksLikeStartOfNextTopLevelDeclaration(Token token) =>
|
||||
token.isTopLevelKeyword ||
|
||||
isOneOfOrEof(
|
||||
token, const ['class', 'const', 'get', 'final', 'set', 'var', 'void']);
|
||||
isOneOfOrEof(token, const ['const', 'get', 'final', 'set', 'var', 'void']);
|
||||
|
|
Loading…
Reference in a new issue