| // 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/src/error/codes.dart'; |
| import 'package:test_reflective_loader/test_reflective_loader.dart'; |
| |
| import '../dart/resolution/context_collection_resolution.dart'; |
| |
| main() { |
| defineReflectiveSuite(() { |
| defineReflectiveTests(AmbiguousExtensionMemberAccessTest); |
| }); |
| } |
| |
| @reflectiveTest |
| class AmbiguousExtensionMemberAccessTest extends PubPackageResolutionTest { |
| test_call() async { |
| await assertErrorsInCode(''' |
| class A {} |
| |
| extension E1 on A { |
| int call() => 0; |
| } |
| |
| extension E2 on A { |
| int call() => 0; |
| } |
| |
| int f(A a) => a(); |
| ''', [ |
| error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 110, 1), |
| ]); |
| } |
| |
| test_getter_getter() async { |
| await assertErrorsInCode(''' |
| extension E1 on int { |
| void get a => 1; |
| } |
| |
| extension E2 on int { |
| void get a => 2; |
| } |
| |
| f() { |
| 0.a; |
| } |
| ''', [ |
| error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 98, 1), |
| ]); |
| |
| var node = findNode.propertyAccess('0.a'); |
| assertResolvedNodeText(node, r''' |
| PropertyAccess |
| target: IntegerLiteral |
| literal: 0 |
| staticType: int |
| operator: . |
| propertyName: SimpleIdentifier |
| token: a |
| staticElement: <null> |
| staticType: InvalidType |
| staticType: InvalidType |
| '''); |
| } |
| |
| test_getter_getterStatic() async { |
| await assertNoErrorsInCode(''' |
| extension E1 on int { |
| void get a => 1; |
| } |
| |
| extension E2 on int { |
| static void get a => 2; |
| } |
| |
| f() { |
| 0.a; |
| } |
| '''); |
| |
| var node = findNode.propertyAccess('0.a'); |
| assertResolvedNodeText(node, r''' |
| PropertyAccess |
| target: IntegerLiteral |
| literal: 0 |
| staticType: int |
| operator: . |
| propertyName: SimpleIdentifier |
| token: a |
| staticElement: <testLibraryFragment>::@extension::E1::@getter::a |
| staticType: void |
| staticType: void |
| '''); |
| } |
| |
| test_getter_method() async { |
| await assertErrorsInCode(''' |
| extension E on int { |
| int get a => 1; |
| } |
| |
| extension E2 on int { |
| void a() {} |
| } |
| |
| f() { |
| 0.a; |
| } |
| ''', [ |
| error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 91, 1), |
| ]); |
| |
| var node = findNode.propertyAccess('0.a'); |
| assertResolvedNodeText(node, r''' |
| PropertyAccess |
| target: IntegerLiteral |
| literal: 0 |
| staticType: int |
| operator: . |
| propertyName: SimpleIdentifier |
| token: a |
| staticElement: <null> |
| staticType: InvalidType |
| staticType: InvalidType |
| '''); |
| } |
| |
| test_getter_setter() async { |
| await assertErrorsInCode(''' |
| extension E on int { |
| int get a => 1; |
| } |
| |
| extension E2 on int { |
| set a(int v) { } |
| } |
| |
| f() { |
| 0.a; |
| } |
| ''', [ |
| error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 96, 1), |
| ]); |
| |
| var node = findNode.propertyAccess('0.a'); |
| assertResolvedNodeText(node, r''' |
| PropertyAccess |
| target: IntegerLiteral |
| literal: 0 |
| staticType: int |
| operator: . |
| propertyName: SimpleIdentifier |
| token: a |
| staticElement: <null> |
| staticType: InvalidType |
| staticType: InvalidType |
| '''); |
| } |
| |
| test_method_conflict_conflict_notSpecific() async { |
| await assertErrorsInCode(''' |
| extension E1 on int { void foo() {} } |
| extension E2 on int { void foo() {} } |
| extension E on int? { void foo() {} } |
| void f() { |
| 0.foo(); |
| } |
| ''', [ |
| error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 129, 3, |
| messageContains: ["in extension 'E1' and extension 'E2',"]), |
| ]); |
| } |
| |
| test_method_conflict_conflict_specific() async { |
| await assertNoErrorsInCode(''' |
| extension E1 on int? { void foo() {} } |
| extension E2 on int? { void foo() {} } |
| extension E on int { void foo() {} } |
| void f() { |
| 0.foo(); |
| } |
| '''); |
| } |
| |
| test_method_conflict_notSpecific_conflict() async { |
| await assertErrorsInCode(''' |
| extension E1 on int { void foo() {} } |
| extension E on int? { void foo() {} } |
| extension E2 on int { void foo() {} } |
| void f() { |
| 0.foo(); |
| } |
| ''', [ |
| error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 129, 3, |
| messageContains: ["in extension 'E1' and extension 'E2',"]), |
| ]); |
| } |
| |
| test_method_conflict_specific_conflict() async { |
| await assertNoErrorsInCode(''' |
| extension E1 on int? { void foo() {} } |
| extension E on int { void foo() {} } |
| extension E2 on int? { void foo() {} } |
| void f() { |
| 0.foo(); |
| } |
| '''); |
| } |
| |
| test_method_method() async { |
| await assertErrorsInCode(''' |
| extension E1 on int { |
| void a() {} |
| } |
| |
| extension E2 on int { |
| void a() {} |
| } |
| |
| f() { |
| 0.a(); |
| } |
| ''', [ |
| error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 88, 1), |
| ]); |
| |
| var node = findNode.methodInvocation('0.a()'); |
| assertResolvedNodeText(node, r''' |
| MethodInvocation |
| target: IntegerLiteral |
| literal: 0 |
| staticType: int |
| operator: . |
| methodName: SimpleIdentifier |
| token: a |
| staticElement: <null> |
| staticType: InvalidType |
| argumentList: ArgumentList |
| leftParenthesis: ( |
| rightParenthesis: ) |
| staticInvokeType: InvalidType |
| staticType: InvalidType |
| '''); |
| } |
| |
| test_method_notSpecific_conflict_conflict() async { |
| await assertErrorsInCode(''' |
| extension E on int? { void foo() {} } |
| extension E1 on int { void foo() {} } |
| extension E2 on int { void foo() {} } |
| void f() { |
| 0.foo(); |
| } |
| ''', [ |
| error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 129, 3, |
| messageContains: ["in extension 'E1' and extension 'E2',"]), |
| ]); |
| } |
| |
| test_method_notSpecific_conflict_conflict_conflict() async { |
| await assertErrorsInCode(''' |
| extension E on int? { void foo() {} } |
| extension E1 on int { void foo() {} } |
| extension E2 on int { void foo() {} } |
| extension E3 on int { void foo() {} } |
| void f() { |
| 0.foo(); |
| } |
| ''', [ |
| error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 167, 3, |
| messageContains: [ |
| "in extension 'E1', extension 'E2', and extension 'E3'," |
| ]), |
| ]); |
| } |
| |
| test_method_specific_conflict_conflict() async { |
| await assertNoErrorsInCode(''' |
| extension E on int { void foo() {} } |
| extension E1 on int? { void foo() {} } |
| extension E2 on int? { void foo() {} } |
| void f() { |
| 0.foo(); |
| } |
| '''); |
| } |
| |
| test_noMoreSpecificExtension() async { |
| await assertErrorsInCode(r''' |
| class Target<T> {} |
| |
| class SubTarget<T> extends Target<T> {} |
| |
| extension E1 on SubTarget<Object> { |
| int get foo => 0; |
| } |
| |
| extension E2<T> on Target<T> { |
| int get foo => 0; |
| } |
| |
| f(SubTarget<num> t) { |
| // The instantiated on type of `E1(t)` is `SubTarget<Object>`. |
| // The instantiated on type of `E2(t)` is `Target<num>`. |
| // Neither is a subtype of the other, so the resolution is ambiguous. |
| t.foo; |
| } |
| ''', [ |
| error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 396, 3), |
| ]); |
| } |
| |
| test_operator_binary() async { |
| await assertErrorsInCode(''' |
| class A {} |
| |
| extension E1 on A { |
| A operator +(A a) => a; |
| } |
| |
| extension E2 on A { |
| A operator +(A a) => a; |
| } |
| |
| A f(A a) => a + a; |
| ''', [ |
| error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 122, 5), |
| ]); |
| } |
| |
| test_operator_binary_compoundAssignment() async { |
| await assertErrorsInCode(''' |
| class A {} |
| |
| extension E1 on A { |
| A operator +(_) => this; |
| } |
| |
| extension E2 on A { |
| A operator +(_) => this; |
| } |
| |
| void f(A a) { |
| a += 0; |
| } |
| ''', [ |
| error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 130, 2), |
| ]); |
| } |
| |
| test_operator_index_index() async { |
| await assertErrorsInCode(''' |
| class A {} |
| |
| extension E1 on A { |
| int operator [](int i) => 0; |
| } |
| |
| extension E2 on A { |
| int operator [](int i) => 0; |
| } |
| |
| int f(A a) => a[0]; |
| ''', [ |
| error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 134, 1), |
| ]); |
| } |
| |
| test_operator_index_indexEq() async { |
| await assertErrorsInCode(''' |
| extension E1 on int { |
| int operator[](int index) => 0; |
| } |
| |
| extension E2 on int { |
| void operator[]=(int index, int value) {} |
| } |
| |
| f() { |
| 0[1] += 2; |
| } |
| ''', [ |
| error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 136, 1), |
| ]); |
| } |
| |
| test_operator_unary() async { |
| await assertErrorsInCode(''' |
| class A {} |
| |
| extension E1 on A { |
| int operator -() => 0; |
| } |
| |
| extension E2 on A { |
| int operator -() => 0; |
| } |
| |
| int f(A a) => -a; |
| ''', [ |
| error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 123, 1), |
| ]); |
| } |
| |
| test_setter_setter() async { |
| await assertErrorsInCode(''' |
| extension E1 on int { |
| set a(x) {} |
| } |
| |
| extension E2 on int { |
| set a(x) {} |
| } |
| |
| f() { |
| 0.a = 3; |
| } |
| ''', [ |
| error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 88, 1), |
| ]); |
| |
| assertResolvedNodeText(findNode.assignment('= 3'), r''' |
| AssignmentExpression |
| leftHandSide: PropertyAccess |
| target: IntegerLiteral |
| literal: 0 |
| staticType: int |
| operator: . |
| propertyName: SimpleIdentifier |
| token: a |
| staticElement: <null> |
| staticType: null |
| staticType: null |
| operator: = |
| rightHandSide: IntegerLiteral |
| literal: 3 |
| parameter: <null> |
| staticType: int |
| readElement: <null> |
| readType: null |
| writeElement: <null> |
| writeType: InvalidType |
| staticElement: <null> |
| staticType: int |
| '''); |
| } |
| |
| test_unnamed_extensions() async { |
| await assertErrorsInCode(''' |
| class A {} |
| class B {} |
| class C extends A implements B {} |
| |
| extension on List<A> { |
| int call() => 0; |
| } |
| |
| extension on List<B> { |
| int call() => 0; |
| } |
| |
| int f(List<C> x) => x(); |
| |
| // Additional calls to avoid UNUSED_ELEMENT |
| int g(List<A> x) => x(); |
| int h(List<B> x) => x(); |
| ''', [ |
| error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 167, 1, |
| messageContains: [ |
| "in unnamed extension on 'List<A>' and unnamed extension on " |
| "'List<B>'," |
| ]), |
| ]); |
| } |
| } |