Improve fasta parser conditional import recovery
Change-Id: I5479078d8d2c3de10b74ed04001fdd852a427c5c
Reviewed-on: https://dart-review.googlesource.com/51561
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Dan Rubel <danrubel@google.com>
diff --git a/pkg/analyzer/lib/src/dart/ast/utilities.dart b/pkg/analyzer/lib/src/dart/ast/utilities.dart
index 3156fe9..56eb475 100644
--- a/pkg/analyzer/lib/src/dart/ast/utilities.dart
+++ b/pkg/analyzer/lib/src/dart/ast/utilities.dart
@@ -1667,6 +1667,7 @@
_isEqualNodeLists(node.metadata, other.metadata) &&
isEqualTokens(node.keyword, other.keyword) &&
isEqualNodes(node.uri, other.uri) &&
+ _isEqualNodeLists(node.configurations, other.configurations) &&
isEqualTokens(node.deferredKeyword, other.deferredKeyword) &&
isEqualTokens(node.asKeyword, other.asKeyword) &&
isEqualNodes(node.prefix, other.prefix) &&
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 d3764ef..eb6dfc6 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
@@ -12,6 +12,8 @@
class ImportDirectivesTest extends PartialCodeTest {
buildAll() {
+ List<String> allExceptEof =
+ PartialCodeTest.prePartSuffixes.map((t) => t.name).toList();
buildTests(
'import_directive',
[
@@ -25,6 +27,55 @@
[ParserErrorCode.EXPECTED_TOKEN], "import '';"),
new TestDescriptor('fullUri', "import 'a.dart'",
[ParserErrorCode.EXPECTED_TOKEN], "import 'a.dart';"),
+ new TestDescriptor(
+ 'if',
+ "import 'a.dart' if",
+ [
+ ParserErrorCode.EXPECTED_TOKEN,
+ ParserErrorCode.EXPECTED_TOKEN,
+ ParserErrorCode.EXPECTED_STRING_LITERAL
+ ],
+ "import 'a.dart' if (_s_) '';"),
+ new TestDescriptor(
+ 'ifParen',
+ "import 'a.dart' if (",
+ [
+ ParserErrorCode.MISSING_IDENTIFIER,
+ ScannerErrorCode.EXPECTED_TOKEN,
+ ParserErrorCode.EXPECTED_STRING_LITERAL,
+ ParserErrorCode.EXPECTED_TOKEN
+ ],
+ "import 'a.dart' if (_s_) '';",
+ failing: allExceptEof),
+ new TestDescriptor(
+ 'ifId',
+ "import 'a.dart' if (b",
+ [
+ ScannerErrorCode.EXPECTED_TOKEN,
+ ParserErrorCode.EXPECTED_TOKEN,
+ ParserErrorCode.EXPECTED_STRING_LITERAL
+ ],
+ "import 'a.dart' if (b) '';",
+ failing: allExceptEof),
+ new TestDescriptor(
+ 'ifEquals',
+ "import 'a.dart' if (b ==",
+ [
+ ParserErrorCode.EXPECTED_STRING_LITERAL,
+ ParserErrorCode.EXPECTED_TOKEN,
+ ScannerErrorCode.EXPECTED_TOKEN,
+ ParserErrorCode.EXPECTED_STRING_LITERAL
+ ],
+ "import 'a.dart' if (b == '') '';",
+ failing: allExceptEof),
+ new TestDescriptor(
+ 'ifCondition',
+ "import 'a.dart' if (b)",
+ [
+ ParserErrorCode.EXPECTED_TOKEN,
+ ParserErrorCode.EXPECTED_STRING_LITERAL
+ ],
+ "import 'a.dart' if (b) '';"),
],
PartialCodeTest.prePartSuffixes);
}
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 d17a6a1..8922d33 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
@@ -68,7 +68,7 @@
}
if (looksLikeStartOfNextDeclaration(identifier) ||
- isOneOfOrEof(identifier, const ['.', '=='])) {
+ isOneOfOrEof(identifier, const ['.', '==', ')'])) {
identifier = parser.insertSyntheticIdentifier(token, this,
message: fasta.templateExpectedIdentifier.withArguments(identifier));
} else {
diff --git a/pkg/front_end/lib/src/fasta/parser/parser.dart b/pkg/front_end/lib/src/fasta/parser/parser.dart
index 0eb697f..394e0d5 100644
--- a/pkg/front_end/lib/src/fasta/parser/parser.dart
+++ b/pkg/front_end/lib/src/fasta/parser/parser.dart
@@ -753,17 +753,50 @@
/// ```
Token parseConditionalUri(Token token) {
Token ifKeyword = token = token.next;
- listener.beginConditionalUri(ifKeyword);
- token = expect('if', token);
- Token leftParen = token;
- expect('(', token);
- token = parseDottedName(token).next;
- Token equalitySign;
- if (optional('==', token)) {
- equalitySign = token;
- token = ensureLiteralString(token).next;
+ assert(optional('if', token));
+ listener.beginConditionalUri(token);
+ Token leftParen = token.next;
+ if (!optional('(', leftParen)) {
+ reportRecoverableError(
+ leftParen, fasta.templateExpectedButGot.withArguments('('));
+
+ int offset = leftParen.charOffset;
+ BeginToken openParen =
+ new SyntheticBeginToken(TokenType.OPEN_PAREN, offset);
+ Token next = openParen
+ .setNext(new SyntheticStringToken(TokenType.IDENTIFIER, '', offset));
+ next = next.setNext(new SyntheticToken(TokenType.CLOSE_PAREN, offset));
+ openParen.endGroup = next;
+
+ token.setNext(openParen);
+ next.setNext(leftParen);
+ leftParen = openParen;
}
- expect(')', token);
+ token = leftParen;
+ token = parseDottedName(token);
+ Token next = token.next;
+ Token equalitySign;
+ if (optional('==', next)) {
+ equalitySign = next;
+ token = ensureLiteralString(next);
+ next = token.next;
+ }
+ if (next != leftParen.endGroup) {
+ reportRecoverableErrorWithToken(next, fasta.templateUnexpectedToken);
+ Token endGroup = leftParen.endGroup;
+ if (endGroup.isSynthetic) {
+ // The scanner did not place the synthetic ')' correctly, so move it.
+
+ // TODO(danrubel): Its costly to find the token before the endGroup.
+ // Consider beforeSynthetic field that points to the previous token
+ // only for synthetic tokens such as ')', '}', ']' so that the parser
+ // can easily move these to the correct location.
+ }
+ next = endGroup;
+ }
+ token = next;
+ assert(optional(')', token));
+
token = ensureLiteralString(token);
listener.endConditionalUri(ifKeyword, leftParen, equalitySign);
return token;
@@ -3175,7 +3208,7 @@
if (!identical(next.kind, STRING_TOKEN)) {
Message message = fasta.templateExpectedString.withArguments(next);
Token newToken =
- new SyntheticStringToken(TokenType.STRING, '""', token.charOffset, 0);
+ new SyntheticStringToken(TokenType.STRING, '""', next.charOffset, 0);
rewriteAndRecover(token, message, newToken);
}
return parseLiteralString(token);