| // Copyright (c) 2023, 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:test_reflective_loader/test_reflective_loader.dart'; |
| |
| import '../../../../client/completion_driver_test.dart'; |
| |
| void main() { |
| defineReflectiveSuite(() { |
| defineReflectiveTests(ExtensionMemberTest); |
| defineReflectiveTests(StaticExtensionMemberTest); |
| }); |
| } |
| |
| @reflectiveTest |
| class ExtensionMemberTest extends AbstractCompletionDriverTest |
| with ExtensionMemberTestCases {} |
| |
| mixin ExtensionMemberTestCases on AbstractCompletionDriverTest { |
| @override |
| bool get includeKeywords => false; |
| |
| @override |
| bool get includeOverrides => false; |
| |
| Future<void> test_extensionOverride_doesNotMatch_partial() async { |
| await computeSuggestions(''' |
| extension E on int { |
| bool a0(int b0, int c0) {} |
| int get b0 => 0; |
| set c0(int d) {} |
| } |
| void f() { |
| E('3').a0^ |
| } |
| '''); |
| assertResponse(r''' |
| replacement |
| left: 2 |
| suggestions |
| a0 |
| kind: methodInvocation |
| '''); |
| } |
| |
| Future<void> test_extensionOverride_matches_partial() async { |
| await computeSuggestions(''' |
| extension E on int { |
| bool a0(int b0, int c0) {} |
| int get b0 => 0; |
| set c0(int d) {} |
| } |
| void f() { |
| E(2).a0^ |
| } |
| '''); |
| assertResponse(r''' |
| replacement |
| left: 2 |
| suggestions |
| a0 |
| kind: methodInvocation |
| '''); |
| } |
| |
| Future<void> test_inClassBody() async { |
| await computeSuggestions(''' |
| class A { |
| ^ |
| } |
| |
| extension on Object { |
| void a0() {} |
| } |
| '''); |
| |
| assertResponse(r''' |
| suggestions |
| '''); |
| } |
| |
| Future<void> test_inClassBody_partial() async { |
| await computeSuggestions(''' |
| class A { |
| a^ |
| } |
| |
| extension on Object { |
| void a0() {} |
| } |
| '''); |
| assertResponse(r''' |
| replacement |
| left: 1 |
| suggestions |
| '''); |
| } |
| |
| Future<void> test_inEnumBody() async { |
| await computeSuggestions(''' |
| enum A { |
| v; |
| ^ |
| } |
| |
| extension on Object { |
| void a0() {} |
| } |
| '''); |
| } |
| |
| Future<void> test_inExtendedClass() async { |
| await computeSuggestions(''' |
| class Person { |
| void doSomething() { |
| ^ |
| } |
| } |
| extension E on Person { |
| String get n0 => ''; |
| set i0(int id) {} |
| void w0() { } |
| } |
| '''); |
| assertResponse(r''' |
| suggestions |
| i0 |
| kind: setter |
| n0 |
| kind: getter |
| w0 |
| kind: methodInvocation |
| '''); |
| } |
| |
| Future<void> test_inExtendedClass_accessedWithThis() async { |
| await computeSuggestions(''' |
| class Person { |
| void doSomething() { |
| this.^ |
| } |
| } |
| extension E on Person { |
| String get n0 => ''; |
| set i0(int i0) {} |
| void w0() { } |
| } |
| '''); |
| assertResponse(r''' |
| suggestions |
| i0 |
| kind: setter |
| n0 |
| kind: getter |
| w0 |
| kind: methodInvocation |
| '''); |
| } |
| |
| Future<void> test_inExtendedClass_multipleExtensions() async { |
| await computeSuggestions(''' |
| class Person { |
| void doSomething() { |
| ^ |
| } |
| } |
| extension on Person { |
| String get n0 => ''; |
| } |
| |
| extension on Person { |
| void w0() { } |
| } |
| |
| '''); |
| assertResponse(r''' |
| suggestions |
| n0 |
| kind: getter |
| w0 |
| kind: methodInvocation |
| '''); |
| } |
| |
| Future<void> test_inExtension_field() async { |
| await computeSuggestions(''' |
| class A { |
| int a0 = 0; |
| } |
| class B extends A { |
| int b0 = 0; |
| } |
| extension E on B { |
| void e() { |
| ^ |
| } |
| } |
| '''); |
| assertResponse(r''' |
| suggestions |
| a0 |
| kind: field |
| b0 |
| kind: field |
| '''); |
| } |
| |
| Future<void> test_inExtension_getterAndSetter() async { |
| await computeSuggestions(''' |
| class A { |
| int get a0 => 0; |
| } |
| class B extends A { |
| set b0(int b0) { } |
| } |
| extension E on B { |
| void e() { |
| ^ |
| } |
| } |
| '''); |
| assertResponse(r''' |
| suggestions |
| a0 |
| kind: getter |
| b0 |
| kind: setter |
| '''); |
| } |
| |
| Future<void> test_inExtension_method() async { |
| await computeSuggestions(''' |
| class A { |
| void a0() { } |
| } |
| class B extends A { |
| void b0() { } |
| } |
| extension E on B { |
| void e() { |
| ^ |
| } |
| } |
| '''); |
| assertResponse(r''' |
| suggestions |
| a0 |
| kind: methodInvocation |
| b0 |
| kind: methodInvocation |
| '''); |
| } |
| |
| Future<void> test_inExtension_methodWithParamType() async { |
| printerConfiguration.withReturnType = true; |
| await computeSuggestions(''' |
| class A<T> { |
| T a0() => null; |
| } |
| |
| extension E on A<int> { |
| void e() { |
| ^ |
| } |
| } |
| '''); |
| assertResponse(r''' |
| suggestions |
| a0 |
| kind: methodInvocation |
| returnType: int |
| '''); |
| } |
| |
| Future<void> test_inExtensionBody() async { |
| await computeSuggestions(''' |
| extension A on int { |
| ^ |
| } |
| |
| extension on Object { |
| void a0() {} |
| } |
| '''); |
| |
| assertResponse(r''' |
| suggestions |
| '''); |
| } |
| |
| Future<void> test_inExtensionTypeBody() async { |
| await computeSuggestions(''' |
| extension type A(int it) { |
| ^ |
| } |
| |
| extension on Object { |
| void a0() {} |
| } |
| '''); |
| |
| assertResponse(r''' |
| suggestions |
| '''); |
| } |
| |
| Future<void> test_inMixinBody() async { |
| await computeSuggestions(''' |
| mixin A { |
| ^ |
| } |
| |
| extension on Object { |
| void a0() {} |
| } |
| '''); |
| assertResponse(r''' |
| suggestions |
| '''); |
| } |
| |
| Future<void> test_inMixinOnExtendedType() async { |
| await computeSuggestions(''' |
| class Person { } |
| extension E on Person { |
| void w0() { } |
| } |
| |
| mixin M on Person { |
| void f() { |
| ^ |
| } |
| } |
| '''); |
| assertResponse(r''' |
| suggestions |
| w0 |
| kind: methodInvocation |
| '''); |
| } |
| |
| Future<void> test_instanceMemberAccess() async { |
| await computeSuggestions(''' |
| extension E on dynamic { |
| void e0() {} |
| } |
| void f(String s) { |
| s.^; |
| } |
| '''); |
| assertResponse(r''' |
| suggestions |
| e0 |
| kind: methodInvocation |
| '''); |
| } |
| |
| Future<void> |
| test_propertyAccess_afterFunctionInvocation_doesNotMatch_partial() async { |
| await computeSuggestions(''' |
| extension E<T extends num> on List<T> { |
| bool a0(int b0, int c0) {} |
| int get b0 => 0; |
| set c0(int d) {} |
| } |
| List<T> g<T>(T x) => [x]; |
| void f(String s) { |
| g(s).a0^ |
| } |
| '''); |
| // The purpose of this test is to verify that nothing is suggested when the |
| // extended type doesn't match. |
| assertResponse(r''' |
| replacement |
| left: 2 |
| suggestions |
| '''); |
| } |
| |
| Future<void> |
| test_propertyAccess_afterIdentifier_doesNotMatch_partial() async { |
| await computeSuggestions(''' |
| extension E<T extends num> on List<T> { |
| bool a0(int b0, int c0) {} |
| int get b0 => 0; |
| set c0(int d) {} |
| } |
| void f(List<String> l) { |
| l.a0^ |
| } |
| '''); |
| // The purpose of this test is to verify that nothing is suggested when the |
| // extended type doesn't match. |
| assertResponse(r''' |
| replacement |
| left: 2 |
| suggestions |
| '''); |
| } |
| |
| Future<void> test_propertyAccess_afterIdentifier_matches_partial() async { |
| await computeSuggestions(''' |
| extension E<T extends num> on List<T> { |
| bool a0(int b0, int c0) {} |
| int get b0 => 0; |
| set c0(int d) {} |
| } |
| void f(List<int> l) { |
| l.a0^ |
| } |
| '''); |
| assertResponse(r''' |
| replacement |
| left: 2 |
| suggestions |
| a0 |
| kind: methodInvocation |
| '''); |
| } |
| |
| Future<void> test_propertyAccess_afterLiteral_doesNotMatch() async { |
| await computeSuggestions(''' |
| extension E on String { |
| bool a0(int b0, int c0) {} |
| int get b0 => 0; |
| set c0(int d) {} |
| } |
| |
| void f() { |
| 0.^ |
| } |
| '''); |
| // The purpose of this test is to assert that none of the extension methods |
| // are suggested. |
| assertResponse(r''' |
| suggestions |
| '''); |
| } |
| |
| Future<void> |
| test_propertyAccess_afterLiteral_doesNotMatch_generic_partial() async { |
| await computeSuggestions(''' |
| extension E<T extends num> on List<T> { |
| bool a0(int b0, int c0) {} |
| int get b0 => 0; |
| set c0(int d) {} |
| } |
| void f() { |
| ['a'].a0^ |
| } |
| '''); |
| // The purpose of this test is to assert that none of the extension methods |
| // are suggested. |
| assertResponse(r''' |
| replacement |
| left: 2 |
| suggestions |
| '''); |
| } |
| |
| Future<void> test_propertyAccess_afterLiteral_matches_partial() async { |
| await computeSuggestions(''' |
| extension E on int { |
| bool a0(int b0, int c0) {} |
| int get b0 => 0; |
| set c0(int d) {} |
| } |
| void f() { |
| 2.a0^ |
| } |
| '''); |
| assertResponse(r''' |
| replacement |
| left: 2 |
| suggestions |
| a0 |
| kind: methodInvocation |
| '''); |
| } |
| |
| Future<void> test_propertyAccess_matches_ignoreNullability() async { |
| await computeSuggestions(''' |
| extension E on int { |
| int get a0 => 0; |
| } |
| void f(int? x) { |
| x.^ |
| } |
| '''); |
| |
| assertResponse(r''' |
| suggestions |
| a0 |
| kind: getter |
| '''); |
| } |
| |
| Future<void> test_propertyAccess_matches_null() async { |
| await computeSuggestions(''' |
| extension E on Null { |
| void f01() {} |
| } |
| |
| void f() { |
| null.^ |
| } |
| '''); |
| assertResponse(r''' |
| suggestions |
| f01 |
| kind: methodInvocation |
| '''); |
| } |
| |
| Future<void> test_propertyAccess_matches_partial() async { |
| await computeSuggestions(''' |
| extension E on int { |
| bool a0(int b0, int c0) {} |
| int get b0 => 0; |
| set c0(int d) {} |
| } |
| void f() { |
| g().a0^ |
| } |
| int g() => 3; |
| '''); |
| assertResponse(r''' |
| replacement |
| left: 2 |
| suggestions |
| a0 |
| kind: methodInvocation |
| '''); |
| } |
| |
| Future<void> test_staticMemberAccess_none() async { |
| allowedIdentifiers = { |
| 'hashCode', |
| 'noSuchMethod', |
| 'runtimeType', |
| 'toString' |
| }; |
| await computeSuggestions(''' |
| extension E on int { |
| void a0() {} |
| } |
| void f() { |
| E.^ |
| } |
| '''); |
| // The purpose of this test is to verify that Type instance members |
| // are not suggested when accessing static members of an extension. |
| assertResponse(r''' |
| suggestions |
| '''); |
| } |
| |
| Future<void> test_staticMemberAccess_none_partial() async { |
| await computeSuggestions(''' |
| extension E on int { |
| void a0() {} |
| } |
| void f() { |
| E.a^ |
| } |
| '''); |
| // The purpose of this test is to verify that there are no suggestions when |
| // there are no static members to suggest. |
| assertResponse(r''' |
| replacement |
| left: 1 |
| suggestions |
| '''); |
| } |
| } |
| |
| @reflectiveTest |
| class StaticExtensionMemberTest extends AbstractCompletionDriverTest |
| with StaticExtensionMemberTestCases {} |
| |
| mixin StaticExtensionMemberTestCases on AbstractCompletionDriverTest { |
| Future<void> test_afterPeriod() async { |
| await computeSuggestions(''' |
| extension E0 on Object { |
| static int i0; |
| static String s0; |
| } |
| void f() { |
| E0.^ |
| } |
| '''); |
| assertResponse(r''' |
| suggestions |
| i0 |
| kind: field |
| s0 |
| kind: field |
| '''); |
| } |
| |
| Future<void> test_afterPeriod_private() async { |
| newFile('$testPackageLibPath/a.dart', ''' |
| extension E on Object { |
| static int _f0 = 0; |
| static String get _g0 => ''; |
| static int _m0() => 0; |
| static set _s0(v) {} |
| void m() { |
| _f0; |
| _g0; |
| _m0(); |
| _s0 = 0; |
| } |
| } |
| '''); |
| await computeSuggestions(''' |
| import 'a.dart'; |
| void f() { |
| E.^; |
| } |
| '''); |
| assertResponse(r''' |
| suggestions |
| '''); |
| } |
| } |