Skip to content

Commit

Permalink
Fix corner cases of type argument parsing.
Browse files Browse the repository at this point in the history
Fixes #46635.

Bug: #46635
Change-Id: I4f74f5ad39bfc775f69e4a47892725e1f166f962
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/207201
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
  • Loading branch information
stereotype441 authored and commit-bot@chromium.org committed Jul 19, 2021
1 parent 9ef9fce commit ee38656
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 3 deletions.
15 changes: 12 additions & 3 deletions pkg/_fe_analyzer_shared/lib/src/parser/parser_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5160,7 +5160,10 @@ class Parser {
// For example a(b)..<T>(c), where token is '<'.
token = typeArg.parseArguments(token, this);
next = token.next!;
assert(optional('(', next));
if (!optional('(', next)) {
listener.handleTypeArgumentApplication(token.next!);
typeArg = noTypeParamOrArg;
}
}
TokenType nextType = next.type;
if (identical(nextType, TokenType.INDEX)) {
Expand Down Expand Up @@ -5307,7 +5310,10 @@ class Parser {
listener.handleNonNullAssertExpression(bangToken);
}
token = typeArg.parseArguments(bangToken, this);
assert(optional('(', token.next!));
if (!optional('(', token.next!)) {
listener.handleTypeArgumentApplication(bangToken.next!);
typeArg = noTypeParamOrArg;
}
}
next = token.next!;
} else if (optional('(', next)) {
Expand All @@ -5327,7 +5333,10 @@ class Parser {
listener.handleNonNullAssertExpression(bangToken);
}
token = typeArg.parseArguments(bangToken, this);
assert(optional('(', token.next!));
if (!optional('(', token.next!)) {
listener.handleTypeArgumentApplication(bangToken.next!);
typeArg = noTypeParamOrArg;
}
}
next = token.next!;
} else {
Expand Down
68 changes: 68 additions & 0 deletions pkg/analyzer/test/generated/function_reference_parser_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,59 @@ class FunctionReferenceParserTest extends FastaParserTestCase {
as MethodInvocation);
}

void test_functionReference_after_indexExpression() {
// Note: this is not legal Dart, but it's important that we do error
// recovery and don't crash the parser.
var functionReference =
parseExpression('x[0]<a, b>', featureSet: constructorTearoffs)
as FunctionReference;
expect(functionReference.function, TypeMatcher<IndexExpression>());
var typeArgs = functionReference.typeArguments!.arguments;
expect(typeArgs, hasLength(2));
expect(((typeArgs[0] as TypeName).name as SimpleIdentifier).name, 'a');
expect(((typeArgs[1] as TypeName).name as SimpleIdentifier).name, 'b');
}

void test_functionReference_after_indexExpression_bang() {
// Note: this is not legal Dart, but it's important that we do error
// recovery and don't crash the parser.
var functionReference =
parseExpression('x[0]!<a, b>', featureSet: constructorTearoffs)
as FunctionReference;
expect(functionReference.function, TypeMatcher<PostfixExpression>());
var typeArgs = functionReference.typeArguments!.arguments;
expect(typeArgs, hasLength(2));
expect(((typeArgs[0] as TypeName).name as SimpleIdentifier).name, 'a');
expect(((typeArgs[1] as TypeName).name as SimpleIdentifier).name, 'b');
}

void test_functionReference_after_indexExpression_functionCall() {
// Note: this is not legal Dart, but it's important that we do error
// recovery and don't crash the parser.
var functionReference =
parseExpression('x[0]()<a, b>', featureSet: constructorTearoffs)
as FunctionReference;
expect(functionReference.function,
TypeMatcher<FunctionExpressionInvocation>());
var typeArgs = functionReference.typeArguments!.arguments;
expect(typeArgs, hasLength(2));
expect(((typeArgs[0] as TypeName).name as SimpleIdentifier).name, 'a');
expect(((typeArgs[1] as TypeName).name as SimpleIdentifier).name, 'b');
}

void test_functionReference_after_indexExpression_nullAware() {
// Note: this is not legal Dart, but it's important that we do error
// recovery and don't crash the parser.
var functionReference =
parseExpression('x?[0]<a, b>', featureSet: constructorTearoffs)
as FunctionReference;
expect(functionReference.function, TypeMatcher<IndexExpression>());
var typeArgs = functionReference.typeArguments!.arguments;
expect(typeArgs, hasLength(2));
expect(((typeArgs[0] as TypeName).name as SimpleIdentifier).name, 'a');
expect(((typeArgs[1] as TypeName).name as SimpleIdentifier).name, 'b');
}

void test_methodTearoff() {
var functionReference =
parseExpression('f().m<a, b>', featureSet: constructorTearoffs)
Expand All @@ -314,6 +367,21 @@ class FunctionReferenceParserTest extends FastaParserTestCase {
expect(((typeArgs[1] as TypeName).name as SimpleIdentifier).name, 'b');
}

void test_methodTearoff_cascaded() {
var cascadeExpression =
parseExpression('f()..m<a, b>', featureSet: constructorTearoffs)
as CascadeExpression;
var functionReference =
cascadeExpression.cascadeSections[0] as FunctionReference;
var function = functionReference.function as PropertyAccess;
expect(function.target, isNull);
expect(function.propertyName.name, 'm');
var typeArgs = functionReference.typeArguments!.arguments;
expect(typeArgs, hasLength(2));
expect(((typeArgs[0] as TypeName).name as SimpleIdentifier).name, 'a');
expect(((typeArgs[1] as TypeName).name as SimpleIdentifier).name, 'b');
}

void test_prefixedIdentifier() {
var functionReference =
parseExpression('prefix.f<a, b>', featureSet: constructorTearoffs)
Expand Down

0 comments on commit ee38656

Please sign in to comment.