Improve type parameter recovery
This fixes another crash in the fasta parser
and improves the way it recovers when parsing type parameters
before a map or list literal.
Change-Id: Ic02513b98a31e9ed3f59c8391c260a2a214983ae
Reviewed-on: https://dart-review.googlesource.com/62340
Commit-Queue: Dan Rubel <danrubel@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analyzer/test/generated/parser_test.dart b/pkg/analyzer/test/generated/parser_test.dart
index 3c85986..19a76ce 100644
--- a/pkg/analyzer/test/generated/parser_test.dart
+++ b/pkg/analyzer/test/generated/parser_test.dart
@@ -4451,16 +4451,11 @@
// It doesn't try to advance past the invalid token `!` to find the
// valid `>`. If it did we'd get less cascading errors, at least for this
// particular example.
- createParser('void m<E, hello!>() {}', expectedEndOffset: 6);
+ createParser('void m<E, hello!>() {}');
ClassMember member = parser.parseClassMember('C');
expectNotNullIfNoErrors(member);
listener.assertErrors(usingFastaParser
- ? [
- // TODO(danrubel): Improve recovery
- expectedError(
- ParserErrorCode.MISSING_METHOD_PARAMETERS, 5, 1) /*<*/,
- expectedError(ParserErrorCode.MISSING_FUNCTION_BODY, 6, 1) /*E*/
- ]
+ ? [expectedError(ParserErrorCode.UNEXPECTED_TOKEN, 15, 1)]
: [
expectedError(ParserErrorCode.EXPECTED_TOKEN, 0, 0) /*>*/,
expectedError(ParserErrorCode.MISSING_IDENTIFIER, 0, 0),
@@ -4470,10 +4465,8 @@
]);
expect(member, new isInstanceOf<MethodDeclaration>());
MethodDeclaration method = member;
- if (!usingFastaParser) {
- expect(method.typeParameters.toString(), '<E, hello>',
- reason: 'parser recovers what it can');
- }
+ expect(method.typeParameters.toString(), '<E, hello>',
+ reason: 'parser recovers what it can');
}
void test_missingAssignableSelector_identifiersAssigned() {
@@ -6758,6 +6751,17 @@
expect(literal.typeArguments, isNotNull);
}
+ void test_parseConstExpression_mapLiteral_typed_missingGt() {
+ Expression expression = parseExpression('const <A, B {}',
+ errors: [expectedError(ParserErrorCode.EXPECTED_TOKEN, 12, 1)]);
+ expect(expression, isNotNull);
+ var literal = expression as MapLiteral;
+ expect(literal.leftBracket, isNotNull);
+ expect(literal.entries, hasLength(0));
+ expect(literal.rightBracket, isNotNull);
+ expect(literal.typeArguments, isNotNull);
+ }
+
void test_parseConstExpression_mapLiteral_typed_genericComment() {
enableGenericMethodComments = true;
Expression expression = parseConstExpression('const /*<A, B>*/ {}');
@@ -6836,6 +6840,17 @@
expect(binaryExpression.rightOperand, isNotNull);
}
+ void test_parseExpression_constAndTypeParameters() {
+ Expression expression = parseExpression('const <E>',
+ codes: usingFastaParser
+ ? [
+ // TODO(danrubel): Improve this error message.
+ ParserErrorCode.EXPECTED_TOKEN
+ ]
+ : [ParserErrorCode.EXPECTED_LIST_OR_MAP_LITERAL]);
+ expect(expression, isNotNull);
+ }
+
void test_parseExpression_function_async() {
Expression expression = parseExpression('() async {}');
var functionExpression = expression as FunctionExpression;
@@ -7022,6 +7037,27 @@
expect((expression.body as ExpressionFunctionBody).semicolon, isNull);
}
+ void test_parseFunctionExpression_constAndTypeParameters2() {
+ FunctionExpression expression =
+ parseFunctionExpression('const <E>(E i) => i++');
+ expect(expression, isNotNull);
+ assertErrorsWithCodes(usingFastaParser
+ ? [ParserErrorCode.UNEXPECTED_TOKEN]
+ : [
+ ParserErrorCode.EXPECTED_TOKEN,
+ ParserErrorCode.EXPECTED_TOKEN,
+ ParserErrorCode.MISSING_IDENTIFIER,
+ ParserErrorCode.FUNCTION_TYPED_PARAMETER_VAR,
+ ParserErrorCode.MISSING_CLOSING_PARENTHESIS,
+ ]);
+ expect(expression.body, isNotNull);
+ if (usingFastaParser) {
+ expect(expression.typeParameters, isNotNull);
+ expect(expression.parameters, isNotNull);
+ expect((expression.body as ExpressionFunctionBody).semicolon, isNull);
+ }
+ }
+
void test_parseFunctionExpression_typeParameterComments() {
enableGenericMethodComments = true;
FunctionExpression expression =
diff --git a/pkg/analyzer/test/src/fasta/recovery/invalid_code_test.dart b/pkg/analyzer/test/src/fasta/recovery/invalid_code_test.dart
index f080fb8..6e680c9 100644
--- a/pkg/analyzer/test/src/fasta/recovery/invalid_code_test.dart
+++ b/pkg/analyzer/test/src/fasta/recovery/invalid_code_test.dart
@@ -34,16 +34,43 @@
''', expectedErrorsInValidCode: [ParserErrorCode.MISSING_IDENTIFIER]);
}
- @failingTest
void test_expressionInPlaceOfTypeName() {
// https://github.com/dart-lang/sdk/issues/30370
testRecovery('''
f() {
return <g('')>[0, 1, 2];
}
-''', [], '''
+''', [ParserErrorCode.UNEXPECTED_TOKEN], '''
f() {
- return <_s_>[0, 1, 2];
+ return <g>[0, 1, 2];
+}
+''');
+ }
+
+ void test_expressionInPlaceOfTypeName2() {
+ // https://github.com/dart-lang/sdk/issues/30370
+ testRecovery('''
+f() {
+ return <test('', (){})>[0, 1, 2];
+}
+''', [ParserErrorCode.UNEXPECTED_TOKEN], '''
+f() {
+ return <test>[0, 1, 2];
+}
+''');
+ }
+
+ @failingTest
+ void test_functionInPlaceOfTypeName() {
+ // https://github.com/dart-lang/sdk/issues/30370
+ // TODO(danrubel): Fix this crash
+ testRecovery('''
+f() {
+ return <test('', (){});>[0, 1, 2];
+}
+''', [ParserErrorCode.UNEXPECTED_TOKEN], '''
+f() {
+ return <test>[0, 1, 2];
}
''');
}
diff --git a/pkg/front_end/lib/src/fasta/parser/parser.dart b/pkg/front_end/lib/src/fasta/parser/parser.dart
index 6178e4b..d12753f 100644
--- a/pkg/front_end/lib/src/fasta/parser/parser.dart
+++ b/pkg/front_end/lib/src/fasta/parser/parser.dart
@@ -4105,25 +4105,31 @@
/// genericFunctionLiteral ::=
/// typeParameters formalParameterList functionBody
/// Provide token for [constKeyword] if preceded by 'const', null if not.
- Token parseLiteralListOrMapOrFunction(Token token, Token constKeyword) {
+ Token parseLiteralListOrMapOrFunction(final Token start, Token constKeyword) {
+ assert(optional('<', start.next));
+ TypeParamOrArgInfo typeParamOrArg = computeTypeParamOrArg(start, true);
+ Token token = typeParamOrArg.skip(start);
Token next = token.next;
- assert(optional('<', next));
- Token closeBrace = next.endGroup;
- if (constKeyword == null &&
- closeBrace != null &&
- identical(closeBrace.next.kind, OPEN_PAREN_TOKEN)) {
- token = computeTypeParamOrArg(token).parseVariables(token, this);
- return parseLiteralFunctionSuffix(token);
- } else {
- token = computeTypeParamOrArg(token).parseArguments(token, this);
- Token next = token.next;
- if (optional('{', next)) {
- return parseLiteralMapSuffix(token, constKeyword);
- } else if ((optional('[', next)) || (optional('[]', next))) {
- return parseLiteralListSuffix(token, constKeyword);
+ if (optional('(', next)) {
+ if (constKeyword != null) {
+ reportRecoverableErrorWithToken(
+ constKeyword, fasta.templateUnexpectedToken);
}
- return reportUnexpectedToken(token.next);
+ token = typeParamOrArg.parseVariables(start, this);
+ return parseLiteralFunctionSuffix(token);
}
+ token = typeParamOrArg.parseArguments(start, this);
+ if (optional('{', next)) {
+ return parseLiteralMapSuffix(token, constKeyword);
+ }
+ if (!optional('[', next) && !optional('[]', next)) {
+ // TODO(danrubel): Improve this error message.
+ reportRecoverableError(
+ next, fasta.templateExpectedButGot.withArguments('['));
+ rewriter.insertTokenAfter(
+ token, new SyntheticToken(TokenType.INDEX, next.charOffset));
+ }
+ return parseLiteralListSuffix(token, constKeyword);
}
/// ```
diff --git a/pkg/front_end/lib/src/fasta/parser/type_info_impl.dart b/pkg/front_end/lib/src/fasta/parser/type_info_impl.dart
index 9a1a945..8d05a29 100644
--- a/pkg/front_end/lib/src/fasta/parser/type_info_impl.dart
+++ b/pkg/front_end/lib/src/fasta/parser/type_info_impl.dart
@@ -603,9 +603,12 @@
assert(optional('>', endGroup) || optional('>>', endGroup));
// If `>>`, then the end or last consumed token is the token before `>>`.
end = optional('>>', next) ? token : next;
- } else if (inDeclaration && start.endGroup == null) {
- // Recovery: Unbalanced `<`
- end = token;
+ } else if (inDeclaration) {
+ end = start.endGroup;
+ if (end == null) {
+ // Recovery: Unbalanced `<`
+ end = token;
+ }
} else {
return noTypeParamOrArg;
}
@@ -614,6 +617,7 @@
@override
Token parseArguments(Token token, Parser parser) {
+ assert(identical(token.next, start));
Token next = start;
Token innerEndGroup = processBeginGroup(start, parser);
parser.listener.beginTypeArguments(start);
@@ -654,6 +658,7 @@
@override
Token parseVariables(Token token, Parser parser) {
+ assert(identical(token.next, start));
Token next = start;
Listener listener = parser.listener;
listener.beginTypeVariables(start);