Files
sdk/pkg/analyzer/test/generated/function_reference_parser_test.dart
T
FMorschel 240eda5647 [analyzer] Migrates to contexMessages from analyzer_testing package
This also includes some small refactorings for parameter names to start using the diagnostic wording instead of error. As well as a small change to `contextMessage` and related to use a list of `Pattern`s instead of a single `String` for better matching.

In future CLs, the TODOs added in this one will be addressed so we can fully migrate this. Added them because of the size of the CL.

Bug: https://github.com/dart-lang/sdk/issues/61557
Change-Id: I5557668a1baed98faff977255c9f22e0d9d96008
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/452180
Commit-Queue: Paul Berry <paulberry@google.com>
Auto-Submit: Felipe Morschel <git@fmorschel.dev>
Reviewed-by: Paul Berry <paulberry@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
2025-10-20 13:37:00 -07:00

502 lines
17 KiB
Dart

// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/src/dart/error/syntactic_errors.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../util/feature_sets.dart';
import 'parser_test_base.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(FunctionReferenceParserTest);
});
}
/// Tests exercising the fasta parser's handling of generic instantiations.
@reflectiveTest
class FunctionReferenceParserTest extends FastaParserTestCase {
/// Verifies that the given [node] matches `f<a, b>`.
void expect_f_a_b(AstNode node) {
var functionReference = node as FunctionReference;
expect((functionReference.function as SimpleIdentifier).name, 'f');
var typeArgs = functionReference.typeArguments!.arguments;
expect(typeArgs, hasLength(2));
expect((typeArgs[0] as NamedType).name.lexeme, 'a');
expect((typeArgs[1] as NamedType).name.lexeme, 'b');
}
void expect_two_args(MethodInvocation methodInvocation) {
var arguments = methodInvocation.argumentList.arguments;
expect(arguments, hasLength(2));
expect(arguments[0], TypeMatcher<BinaryExpression>());
expect(arguments[1], TypeMatcher<BinaryExpression>());
}
void test_feature_disabled() {
expect_f_a_b(
(parseStatement('f<a, b>;', featureSet: FeatureSets.language_2_13)
as ExpressionStatement)
.expression,
);
listener.assertErrors([
expectedError(ParserErrorCode.experimentNotEnabled, 1, 6),
]);
}
void test_followingToken_accepted_closeBrace() {
expect_f_a_b((parseExpression('{f<a, b>}') as SetOrMapLiteral).elements[0]);
}
void test_followingToken_accepted_closeBracket() {
expect_f_a_b((parseExpression('[f<a, b>]') as ListLiteral).elements[0]);
}
void test_followingToken_accepted_closeParen() {
expect_f_a_b(
(parseExpression('g(f<a, b>)') as MethodInvocation)
.argumentList
.arguments[0],
);
}
void test_followingToken_accepted_colon() {
expect_f_a_b(
((parseExpression('{f<a, b>: null}') as SetOrMapLiteral).elements[0]
as MapLiteralEntry)
.key,
);
}
void test_followingToken_accepted_comma() {
expect_f_a_b(
(parseExpression('[f<a, b>, null]') as ListLiteral).elements[0],
);
}
void test_followingToken_accepted_equals() {
expect_f_a_b(
(parseExpression('f<a, b> == null') as BinaryExpression).leftOperand,
);
}
void test_followingToken_accepted_not_equals() {
expect_f_a_b(
(parseExpression('f<a, b> != null') as BinaryExpression).leftOperand,
);
}
void test_followingToken_accepted_openParen() {
// This is a special case because when a `(` follows `<typeArguments>` it is
// parsed as a MethodInvocation rather than a GenericInstantiation.
var methodInvocation = parseExpression('f<a, b>()') as MethodInvocation;
expect(methodInvocation.methodName.name, 'f');
var typeArgs = methodInvocation.typeArguments!.arguments;
expect(typeArgs, hasLength(2));
expect((typeArgs[0] as NamedType).name.lexeme, 'a');
expect((typeArgs[1] as NamedType).name.lexeme, 'b');
expect(methodInvocation.argumentList.arguments, isEmpty);
}
void test_followingToken_accepted_period_methodInvocation() {
// This is a special case because `f<a, b>.methodName(...)` is parsed as an
// InstanceCreationExpression.
var instanceCreationExpression =
parseExpression('f<a, b>.toString()') as InstanceCreationExpression;
var constructorName = instanceCreationExpression.constructorName;
var type = constructorName.type;
expect(type.name.lexeme, 'f');
var typeArgs = type.typeArguments!.arguments;
expect(typeArgs, hasLength(2));
expect((typeArgs[0] as NamedType).name.lexeme, 'a');
expect((typeArgs[1] as NamedType).name.lexeme, 'b');
expect(constructorName.name!.name, 'toString');
expect(instanceCreationExpression.argumentList.arguments, isEmpty);
}
void test_followingToken_accepted_period_methodInvocation_generic() {
expect_f_a_b(
(parseExpression('f<a, b>.foo<c>()') as MethodInvocation).target!,
);
}
void test_followingToken_accepted_period_propertyAccess() {
expect_f_a_b(
(parseExpression('f<a, b>.hashCode') as PropertyAccess).target!,
);
}
void test_followingToken_accepted_semicolon() {
expect_f_a_b(
(parseStatement('f<a, b>;') as ExpressionStatement).expression,
);
listener.assertNoErrors();
}
void test_followingToken_rejected_ampersand() {
expect_two_args(
parseExpression(
'f(a<b,c>&d)',
diagnostics: [
expectedError(ParserErrorCode.missingIdentifier, 8, 1),
],
)
as MethodInvocation,
);
}
void test_followingToken_rejected_as() {
expect_two_args(parseExpression('f(a<b,c>as)') as MethodInvocation);
}
void test_followingToken_rejected_asterisk() {
expect_two_args(
parseExpression(
'f(a<b,c>*d)',
diagnostics: [
expectedError(ParserErrorCode.missingIdentifier, 8, 1),
],
)
as MethodInvocation,
);
}
void test_followingToken_rejected_bang_openBracket() {
expect_two_args(parseExpression('f(a<b,c>![d])') as MethodInvocation);
}
void test_followingToken_rejected_bang_paren() {
expect_two_args(parseExpression('f(a<b,c>!(d))') as MethodInvocation);
}
void test_followingToken_rejected_bar() {
expect_two_args(
parseExpression(
'f(a<b,c>|d)',
diagnostics: [
expectedError(ParserErrorCode.missingIdentifier, 8, 1),
],
)
as MethodInvocation,
);
}
void test_followingToken_rejected_caret() {
expect_two_args(
parseExpression(
'f(a<b,c>^d)',
diagnostics: [
expectedError(ParserErrorCode.missingIdentifier, 8, 1),
],
)
as MethodInvocation,
);
}
void test_followingToken_rejected_is() {
var methodInvocation =
parseExpression(
'f(a<b,c> is int)',
diagnostics: [
expectedError(ParserErrorCode.missingIdentifier, 9, 2),
],
)
as MethodInvocation;
var arguments = methodInvocation.argumentList.arguments;
expect(arguments, hasLength(2));
expect(arguments[0], TypeMatcher<BinaryExpression>());
expect(arguments[1], TypeMatcher<IsExpression>());
}
void test_followingToken_rejected_lessThan() {
// Note: in principle we could parse this as a generic instantiation of a
// generic instantiation, but such an expression would be meaningless so we
// reject it at the parser level.
parseExpression(
'f<a><b>',
diagnostics: [
expectedError(ParserErrorCode.equalityCannotBeEqualityOperand, 3, 1),
expectedError(ParserErrorCode.expectedToken, 7, 0),
],
);
}
void test_followingToken_rejected_minus() {
expect_two_args(parseExpression('f(a<b,c>-d)') as MethodInvocation);
}
void test_followingToken_rejected_openBracket() {
expect_two_args(parseExpression('f(a<b,c>[d])') as MethodInvocation);
}
void test_followingToken_rejected_openBracket_error() {
// Note that theoretically this could be successfully parsed by interpreting
// `<` and `>` as delimiting type arguments, but the parser doesn't have
// enough lookahead to see that this is the only possible error-free parse;
// it commits to interpreting `<` and `>` as operators when it sees the `[`.
expect_two_args(
parseExpression(
'f(a<b,c>[d]>e)',
diagnostics: [
expectedError(
ParserErrorCode.equalityCannotBeEqualityOperand,
11,
1,
),
],
)
as MethodInvocation,
);
}
void test_followingToken_rejected_openBracket_unambiguous() {
expect_two_args(parseExpression('f(a<b,c>[d, e])') as MethodInvocation);
}
void test_followingToken_rejected_percent() {
expect_two_args(
parseExpression(
'f(a<b,c>%d)',
diagnostics: [
expectedError(ParserErrorCode.missingIdentifier, 8, 1),
],
)
as MethodInvocation,
);
}
void test_followingToken_rejected_period_period() {
var methodInvocation =
parseExpression(
'f(a<b,c>..toString())',
diagnostics: [
expectedError(ParserErrorCode.missingIdentifier, 8, 2),
],
)
as MethodInvocation;
var arguments = methodInvocation.argumentList.arguments;
expect(arguments, hasLength(2));
expect(arguments[0], TypeMatcher<BinaryExpression>());
expect(arguments[1], TypeMatcher<CascadeExpression>());
}
void test_followingToken_rejected_plus() {
expect_two_args(
parseExpression(
'f(a<b,c>+d)',
diagnostics: [
expectedError(ParserErrorCode.missingIdentifier, 8, 1),
],
)
as MethodInvocation,
);
}
void test_followingToken_rejected_question() {
var methodInvocation =
parseExpression(
'f(a<b,c> ? null : null)',
diagnostics: [
expectedError(ParserErrorCode.missingIdentifier, 9, 1),
],
)
as MethodInvocation;
var arguments = methodInvocation.argumentList.arguments;
expect(arguments, hasLength(2));
expect(arguments[0], TypeMatcher<BinaryExpression>());
expect(arguments[1], TypeMatcher<ConditionalExpression>());
}
void test_followingToken_rejected_question_period_methodInvocation() {
expect_two_args(
parseExpression(
'f(a<b,c>?.toString())',
diagnostics: [
expectedError(ParserErrorCode.missingIdentifier, 8, 2),
],
)
as MethodInvocation,
);
}
void test_followingToken_rejected_question_period_methodInvocation_generic() {
expect_two_args(
parseExpression(
'f(a<b,c>?.foo<c>())',
diagnostics: [
expectedError(ParserErrorCode.missingIdentifier, 8, 2),
],
)
as MethodInvocation,
);
}
void test_followingToken_rejected_question_period_period() {
var methodInvocation =
parseExpression(
'f(a<b,c>?..toString())',
diagnostics: [
expectedError(ParserErrorCode.missingIdentifier, 8, 3),
expectedError(ParserErrorCode.expectedToken, 11, 8),
],
)
as MethodInvocation;
var arguments = methodInvocation.argumentList.arguments;
expect(arguments, hasLength(3));
expect(arguments[0], TypeMatcher<BinaryExpression>());
expect(arguments[1], TypeMatcher<BinaryExpression>());
expect(arguments[2], TypeMatcher<MethodInvocation>());
}
void test_followingToken_rejected_question_period_propertyAccess() {
expect_two_args(
parseExpression(
'f(a<b,c>?.hashCode)',
diagnostics: [
expectedError(ParserErrorCode.missingIdentifier, 8, 2),
],
)
as MethodInvocation,
);
}
void test_followingToken_rejected_question_question() {
expect_two_args(
parseExpression(
'f(a<b,c> ?? d)',
diagnostics: [
expectedError(ParserErrorCode.missingIdentifier, 9, 2),
],
)
as MethodInvocation,
);
}
void test_followingToken_rejected_slash() {
expect_two_args(
parseExpression(
'f(a<b,c>/d)',
diagnostics: [
expectedError(ParserErrorCode.missingIdentifier, 8, 1),
],
)
as MethodInvocation,
);
}
void test_followingToken_rejected_tilde_slash() {
expect_two_args(
parseExpression(
'f(a<b,c>~/d)',
diagnostics: [
expectedError(ParserErrorCode.missingIdentifier, 8, 2),
],
)
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>') as FunctionReference;
expect(functionReference.function, TypeMatcher<IndexExpression>());
var typeArgs = functionReference.typeArguments!.arguments;
expect(typeArgs, hasLength(2));
expect((typeArgs[0] as NamedType).name.lexeme, 'a');
expect((typeArgs[1] as NamedType).name.lexeme, '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>') as FunctionReference;
expect(functionReference.function, TypeMatcher<PostfixExpression>());
var typeArgs = functionReference.typeArguments!.arguments;
expect(typeArgs, hasLength(2));
expect((typeArgs[0] as NamedType).name.lexeme, 'a');
expect((typeArgs[1] as NamedType).name.lexeme, '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>') as FunctionReference;
expect(
functionReference.function,
TypeMatcher<FunctionExpressionInvocation>(),
);
var typeArgs = functionReference.typeArguments!.arguments;
expect(typeArgs, hasLength(2));
expect((typeArgs[0] as NamedType).name.lexeme, 'a');
expect((typeArgs[1] as NamedType).name.lexeme, '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>') as FunctionReference;
expect(functionReference.function, TypeMatcher<IndexExpression>());
var typeArgs = functionReference.typeArguments!.arguments;
expect(typeArgs, hasLength(2));
expect((typeArgs[0] as NamedType).name.lexeme, 'a');
expect((typeArgs[1] as NamedType).name.lexeme, 'b');
}
void test_methodTearoff() {
var functionReference = parseExpression('f().m<a, b>') as FunctionReference;
var function = functionReference.function as PropertyAccess;
var target = function.target as MethodInvocation;
expect(target.methodName.name, 'f');
expect(function.propertyName.name, 'm');
var typeArgs = functionReference.typeArguments!.arguments;
expect(typeArgs, hasLength(2));
expect((typeArgs[0] as NamedType).name.lexeme, 'a');
expect((typeArgs[1] as NamedType).name.lexeme, 'b');
}
void test_methodTearoff_cascaded() {
var cascadeExpression =
parseExpression('f()..m<a, b>') 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 NamedType).name.lexeme, 'a');
expect((typeArgs[1] as NamedType).name.lexeme, 'b');
}
void test_prefixedIdentifier() {
var functionReference =
parseExpression('prefix.f<a, b>') as FunctionReference;
var function = functionReference.function as PrefixedIdentifier;
expect(function.prefix.name, 'prefix');
expect(function.identifier.name, 'f');
var typeArgs = functionReference.typeArguments!.arguments;
expect(typeArgs, hasLength(2));
expect((typeArgs[0] as NamedType).name.lexeme, 'a');
expect((typeArgs[1] as NamedType).name.lexeme, 'b');
}
void test_three_identifiers() {
var functionReference =
parseExpression('prefix.ClassName.m<a, b>') as FunctionReference;
var function = functionReference.function as PropertyAccess;
var target = function.target as PrefixedIdentifier;
expect(target.prefix.name, 'prefix');
expect(target.identifier.name, 'ClassName');
expect(function.propertyName.name, 'm');
var typeArgs = functionReference.typeArguments!.arguments;
expect(typeArgs, hasLength(2));
expect((typeArgs[0] as NamedType).name.lexeme, 'a');
expect((typeArgs[1] as NamedType).name.lexeme, 'b');
}
}