| // 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/error/codes.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 = 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_functionExpressionInvocation() 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.functionExpressionInvocation('g(0)'); |
| assertElementNull(invocation); |
| assertInvokeType(invocation, 'double Function(int)'); |
| assertType(invocation, 'double'); |
| |
| var function = invocation.function as PropertyAccess; |
| assertElement(function.propertyName, findElement.getter('g', of: 'E')); |
| assertType(function.propertyName, 'double Function(int)'); |
| } |
| |
| 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_onTearoff() async { |
| // https://github.com/dart-lang/sdk/issues/38653 |
| await assertErrorsInCode(''' |
| f(){ |
| E(null).v++; |
| } |
| extension E on Object{ |
| v() {} |
| } |
| ''', [ |
| error(CompileTimeErrorCode.UNDEFINED_EXTENSION_SETTER, 15, 1), |
| ]); |
| findDeclarationAndOverride( |
| declarationName: 'E ', overrideSearch: 'E(null)'); |
| validateOverride(); |
| } |
| |
| 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); |
| } |
| } |