blob: e76fb1be24db6431a826a83bea1c65ac466cd503 [file] [log] [blame]
// Copyright (c) 2019, 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/analysis/features.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:meta/meta.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'driver_resolution.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(ExtensionOverrideTest);
});
}
@reflectiveTest
class ExtensionOverrideTest extends DriverResolutionTest {
ExtensionElement extension;
ExtensionOverride extensionOverride;
@override
AnalysisOptionsImpl get analysisOptions => AnalysisOptionsImpl()
..contextFeatures = new FeatureSet.forTesting(
sdkVersion: '2.3.0', additionalFeatures: [Feature.extension_methods]);
void findDeclarationAndOverride(
{@required String declarationName,
@required String overrideSearch,
String declarationUri}) {
if (declarationUri == null) {
ExtensionDeclaration declaration =
findNode.extensionDeclaration('extension $declarationName');
extension = declaration?.declaredElement;
} else {
extension =
findElement.importFind(declarationUri).extension_(declarationName);
}
extensionOverride = findNode.extensionOverride(overrideSearch);
}
test_call_noPrefix_noTypeArguments() async {
await assertNoErrorsInCode('''
class A {}
extension E on A {
int call(String s) => 0;
}
void f(A a) {
E(a)('');
}
''');
findDeclarationAndOverride(declarationName: 'E ', overrideSearch: 'E(a)');
validateOverride();
validateCall();
}
test_call_noPrefix_typeArguments() async {
// The test is failing because we're not yet doing type inference.
await assertNoErrorsInCode('''
class A {}
extension E<T> on A {
int call(T s) => 0;
}
void f(A a) {
E<String>(a)('');
}
''');
findDeclarationAndOverride(declarationName: 'E<T>', overrideSearch: 'E<S');
validateOverride(typeArguments: [stringType]);
validateCall();
}
test_call_prefix_noTypeArguments() async {
newFile('/test/lib/lib.dart', content: '''
class A {}
extension E on A {
int call(String s) => 0;
}
''');
await assertNoErrorsInCode('''
import 'lib.dart' as p;
void f(p.A a) {
p.E(a)('');
}
''');
findDeclarationAndOverride(
declarationName: 'E',
declarationUri: 'package:test/lib.dart',
overrideSearch: 'E(a)');
validateOverride();
validateCall();
}
test_call_prefix_typeArguments() async {
// The test is failing because we're not yet doing type inference.
newFile('/test/lib/lib.dart', content: '''
class A {}
extension E<T> on A {
int call(T s) => 0;
}
''');
await assertNoErrorsInCode('''
import 'lib.dart' as p;
void f(p.A a) {
p.E<String>(a)('');
}
''');
findDeclarationAndOverride(
declarationName: 'E',
declarationUri: 'package:test/lib.dart',
overrideSearch: 'E<S');
validateOverride(typeArguments: [stringType]);
validateCall();
}
test_getter_noPrefix_noTypeArguments() async {
await assertNoErrorsInCode('''
class A {}
extension E on A {
int get g => 0;
}
void f(A a) {
E(a).g;
}
''');
findDeclarationAndOverride(declarationName: 'E ', overrideSearch: 'E(a)');
validateOverride();
validatePropertyAccess();
}
test_getter_noPrefix_noTypeArguments_methodInvocation() async {
await assertNoErrorsInCode('''
class A {}
extension E on A {
double Function(int) get g => (b) => 2.0;
}
void f(A a) {
E(a).g(0);
}
''');
findDeclarationAndOverride(declarationName: 'E ', overrideSearch: 'E(a)');
validateOverride();
var invocation = findNode.methodInvocation('g(0);');
assertMethodInvocation(
invocation,
findElement.getter('g'),
'double Function(int)',
expectedMethodNameType: 'double Function(int) Function()',
);
}
test_getter_noPrefix_typeArguments() async {
await assertNoErrorsInCode('''
class A {}
extension E<T> on A {
int get g => 0;
}
void f(A a) {
E<int>(a).g;
}
''');
findDeclarationAndOverride(declarationName: 'E', overrideSearch: 'E<int>');
validateOverride(typeArguments: [intType]);
validatePropertyAccess();
}
test_getter_prefix_noTypeArguments() async {
newFile('/test/lib/lib.dart', content: '''
class A {}
extension E on A {
int get g => 0;
}
''');
await assertNoErrorsInCode('''
import 'lib.dart' as p;
void f(p.A a) {
p.E(a).g;
}
''');
findDeclarationAndOverride(
declarationName: 'E',
declarationUri: 'package:test/lib.dart',
overrideSearch: 'E(a)');
validateOverride();
validatePropertyAccess();
}
test_getter_prefix_typeArguments() async {
newFile('/test/lib/lib.dart', content: '''
class A {}
extension E<T> on A {
int get g => 0;
}
''');
await assertNoErrorsInCode('''
import 'lib.dart' as p;
void f(p.A a) {
p.E<int>(a).g;
}
''');
findDeclarationAndOverride(
declarationName: 'E',
declarationUri: 'package:test/lib.dart',
overrideSearch: 'E<int>');
validateOverride(typeArguments: [intType]);
validatePropertyAccess();
}
test_method_noPrefix_noTypeArguments() async {
await assertNoErrorsInCode('''
class A {}
extension E on A {
void m() {}
}
void f(A a) {
E(a).m();
}
''');
findDeclarationAndOverride(declarationName: 'E ', overrideSearch: 'E(a)');
validateOverride();
validateInvocation();
}
test_method_noPrefix_typeArguments() async {
await assertNoErrorsInCode('''
class A {}
extension E<T> on A {
void m() {}
}
void f(A a) {
E<int>(a).m();
}
''');
findDeclarationAndOverride(declarationName: 'E', overrideSearch: 'E<int>');
validateOverride(typeArguments: [intType]);
validateInvocation();
}
test_method_prefix_noTypeArguments() async {
newFile('/test/lib/lib.dart', content: '''
class A {}
extension E on A {
void m() {}
}
''');
await assertNoErrorsInCode('''
import 'lib.dart' as p;
void f(p.A a) {
p.E(a).m();
}
''');
findDeclarationAndOverride(
declarationName: 'E',
declarationUri: 'package:test/lib.dart',
overrideSearch: 'E(a)');
validateOverride();
validateInvocation();
}
test_method_prefix_typeArguments() async {
newFile('/test/lib/lib.dart', content: '''
class A {}
extension E<T> on A {
void m() {}
}
''');
await assertNoErrorsInCode('''
import 'lib.dart' as p;
void f(p.A a) {
p.E<int>(a).m();
}
''');
findDeclarationAndOverride(
declarationName: 'E',
declarationUri: 'package:test/lib.dart',
overrideSearch: 'E<int>');
validateOverride(typeArguments: [intType]);
validateInvocation();
}
test_operator_noPrefix_noTypeArguments() async {
await assertNoErrorsInCode('''
class A {}
extension E on A {
void operator +(int offset) {}
}
void f(A a) {
E(a) + 1;
}
''');
findDeclarationAndOverride(declarationName: 'E ', overrideSearch: 'E(a)');
validateOverride();
validateBinaryExpression();
}
test_operator_noPrefix_typeArguments() async {
await assertNoErrorsInCode('''
class A {}
extension E<T> on A {
void operator +(int offset) {}
}
void f(A a) {
E<int>(a) + 1;
}
''');
findDeclarationAndOverride(declarationName: 'E', overrideSearch: 'E<int>');
validateOverride(typeArguments: [intType]);
validateBinaryExpression();
}
test_operator_prefix_noTypeArguments() async {
newFile('/test/lib/lib.dart', content: '''
class A {}
extension E on A {
void operator +(int offset) {}
}
''');
await assertNoErrorsInCode('''
import 'lib.dart' as p;
void f(p.A a) {
p.E(a) + 1;
}
''');
findDeclarationAndOverride(
declarationName: 'E',
declarationUri: 'package:test/lib.dart',
overrideSearch: 'E(a)');
validateOverride();
validateBinaryExpression();
}
test_operator_prefix_typeArguments() async {
newFile('/test/lib/lib.dart', content: '''
class A {}
extension E<T> on A {
void operator +(int offset) {}
}
''');
await assertNoErrorsInCode('''
import 'lib.dart' as p;
void f(p.A a) {
p.E<int>(a) + 1;
}
''');
findDeclarationAndOverride(
declarationName: 'E',
declarationUri: 'package:test/lib.dart',
overrideSearch: 'E<int>');
validateOverride(typeArguments: [intType]);
validateBinaryExpression();
}
test_setter_noPrefix_noTypeArguments() async {
await assertNoErrorsInCode('''
class A {}
extension E on A {
set s(int x) {}
}
void f(A a) {
E(a).s = 0;
}
''');
findDeclarationAndOverride(declarationName: 'E ', overrideSearch: 'E(a)');
validateOverride();
validatePropertyAccess();
}
test_setter_noPrefix_typeArguments() async {
await assertNoErrorsInCode('''
class A {}
extension E<T> on A {
set s(int x) {}
}
void f(A a) {
E<int>(a).s = 0;
}
''');
findDeclarationAndOverride(declarationName: 'E', overrideSearch: 'E<int>');
validateOverride(typeArguments: [intType]);
validatePropertyAccess();
}
test_setter_prefix_noTypeArguments() async {
newFile('/test/lib/lib.dart', content: '''
class A {}
extension E on A {
set s(int x) {}
}
''');
await assertNoErrorsInCode('''
import 'lib.dart' as p;
void f(p.A a) {
p.E(a).s = 0;
}
''');
findDeclarationAndOverride(
declarationName: 'E',
declarationUri: 'package:test/lib.dart',
overrideSearch: 'E(a)');
validateOverride();
validatePropertyAccess();
}
test_setter_prefix_typeArguments() async {
newFile('/test/lib/lib.dart', content: '''
class A {}
extension E<T> on A {
set s(int x) {}
}
''');
await assertNoErrorsInCode('''
import 'lib.dart' as p;
void f(p.A a) {
p.E<int>(a).s = 0;
}
''');
findDeclarationAndOverride(
declarationName: 'E',
declarationUri: 'package:test/lib.dart',
overrideSearch: 'E<int>');
validateOverride(typeArguments: [intType]);
validatePropertyAccess();
}
test_setterAndGetter_noPrefix_noTypeArguments() async {
await assertNoErrorsInCode('''
class A {}
extension E on A {
int get s => 0;
set s(int x) {}
}
void f(A a) {
E(a).s += 0;
}
''');
findDeclarationAndOverride(declarationName: 'E ', overrideSearch: 'E(a)');
validateOverride();
validatePropertyAccess();
}
test_setterAndGetter_noPrefix_typeArguments() async {
await assertNoErrorsInCode('''
class A {}
extension E<T> on A {
int get s => 0;
set s(int x) {}
}
void f(A a) {
E<int>(a).s += 0;
}
''');
findDeclarationAndOverride(declarationName: 'E', overrideSearch: 'E<int>');
validateOverride(typeArguments: [intType]);
validatePropertyAccess();
}
test_setterAndGetter_prefix_noTypeArguments() async {
newFile('/test/lib/lib.dart', content: '''
class A {}
extension E on A {
int get s => 0;
set s(int x) {}
}
''');
await assertNoErrorsInCode('''
import 'lib.dart' as p;
void f(p.A a) {
p.E(a).s += 0;
}
''');
findDeclarationAndOverride(
declarationName: 'E',
declarationUri: 'package:test/lib.dart',
overrideSearch: 'E(a)');
validateOverride();
validatePropertyAccess();
}
test_setterAndGetter_prefix_typeArguments() async {
newFile('/test/lib/lib.dart', content: '''
class A {}
extension E<T> on A {
int get s => 0;
set s(int x) {}
}
''');
await assertNoErrorsInCode('''
import 'lib.dart' as p;
void f(p.A a) {
p.E<int>(a).s += 0;
}
''');
findDeclarationAndOverride(
declarationName: 'E',
declarationUri: 'package:test/lib.dart',
overrideSearch: 'E<int>');
validateOverride(typeArguments: [intType]);
validatePropertyAccess();
}
test_tearoff() async {
await assertNoErrorsInCode('''
class C {}
extension E on C {
void a(int x) {}
}
f(C c) => E(c).a;
''');
var identifier = findNode.simple('a;');
assertElement(identifier, findElement.method('a'));
assertType(identifier, 'void Function(int)');
}
void validateBinaryExpression() {
BinaryExpression binary = extensionOverride.parent as BinaryExpression;
Element resolvedElement = binary.staticElement;
expect(resolvedElement, extension.getMethod('+'));
}
void validateCall() {
FunctionExpressionInvocation invocation =
extensionOverride.parent as FunctionExpressionInvocation;
Element resolvedElement = invocation.staticElement;
expect(resolvedElement, extension.getMethod('call'));
NodeList<Expression> arguments = invocation.argumentList.arguments;
for (int i = 0; i < arguments.length; i++) {
expect(arguments[i].staticParameterElement, isNotNull);
}
}
void validateInvocation() {
MethodInvocation invocation = extensionOverride.parent as MethodInvocation;
assertMethodInvocation(
invocation,
extension.getMethod('m'),
'void Function()',
);
NodeList<Expression> arguments = invocation.argumentList.arguments;
for (int i = 0; i < arguments.length; i++) {
expect(arguments[i].staticParameterElement, isNotNull);
}
}
void validateOverride({List<DartType> typeArguments}) {
expect(extensionOverride.extensionName.staticElement, extension);
expect(extensionOverride.staticType, isNull);
expect(extensionOverride.extensionName.staticType, isNull);
if (typeArguments == null) {
expect(extensionOverride.typeArguments, isNull);
} else {
expect(
extensionOverride.typeArguments.arguments
.map((annotation) => annotation.type),
unorderedEquals(typeArguments));
}
expect(extensionOverride.argumentList.arguments, hasLength(1));
}
void validatePropertyAccess() {
PropertyAccess access = extensionOverride.parent as PropertyAccess;
Element resolvedElement = access.propertyName.staticElement;
PropertyAccessorElement expectedElement;
if (access.propertyName.inSetterContext()) {
expectedElement = extension.getSetter('s');
if (access.propertyName.inGetterContext()) {
PropertyAccessorElement expectedGetter = extension.getGetter('s');
Element actualGetter =
access.propertyName.auxiliaryElements.staticElement;
expect(actualGetter, expectedGetter);
}
} else {
expectedElement = extension.getGetter('g');
}
expect(resolvedElement, expectedElement);
}
}