| // 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(TypeArgumentNotMatchingBoundsTest); |
| }); |
| } |
| |
| @reflectiveTest |
| class TypeArgumentNotMatchingBoundsTest extends PubPackageResolutionTest { |
| test_classTypeAlias() async { |
| await assertErrorsInCode(r''' |
| class A {} |
| class B {} |
| mixin C {} |
| class G<E extends A> {} |
| class D = G<B> with C; |
| ''', [ |
| error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 69, 1), |
| ]); |
| } |
| |
| test_const() async { |
| await assertErrorsInCode(r''' |
| class A {} |
| class B {} |
| class G<E extends A> { |
| const G(); |
| } |
| f() { return const G<B>(); } |
| ''', [ |
| error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 81, 1), |
| ]); |
| } |
| |
| test_const_matching() async { |
| await assertNoErrorsInCode(r''' |
| class A {} |
| class B extends A {} |
| class G<E extends A> { |
| const G(); |
| } |
| f() { return const G<B>(); } |
| '''); |
| } |
| |
| test_enum_inferred() async { |
| await assertErrorsInCode(''' |
| enum E<T extends int> { |
| v(''); |
| const E(T t); |
| } |
| ''', [ |
| error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 26, 1), |
| ]); |
| } |
| |
| test_enum_superBounded() async { |
| await assertNoErrorsInCode(''' |
| enum E<T extends E<T>> { |
| v<Never>() |
| } |
| '''); |
| } |
| |
| test_enum_withTypeArguments() async { |
| await assertErrorsInCode(''' |
| enum E<T extends int> { |
| v<String>() |
| } |
| ''', [ |
| error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 28, 6), |
| ]); |
| } |
| |
| test_extends() async { |
| await assertErrorsInCode(r''' |
| class A {} |
| class B {} |
| class G<E extends A> {} |
| class C extends G<B>{} |
| ''', [ |
| error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 64, 1), |
| ]); |
| } |
| |
| test_extends_regressionInIssue18468Fix() async { |
| // https://code.google.com/p/dart/issues/detail?id=18628 |
| await assertErrorsInCode(r''' |
| class X<T extends Type> {} |
| class Y<U> extends X<U> {} |
| ''', [ |
| error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 48, 1), |
| ]); |
| } |
| |
| test_extensionOverride_hasTypeArguments() async { |
| await assertErrorsInCode(r''' |
| extension E<T extends num> on int { |
| void foo() {} |
| } |
| |
| void f() { |
| E<String>(0).foo(); |
| } |
| ''', [ |
| error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 70, 6), |
| ]); |
| } |
| |
| test_extensionOverride_hasTypeArguments_call() async { |
| await assertErrorsInCode(r''' |
| extension E<T extends num> on int { |
| void call() {} |
| } |
| |
| void f() { |
| E<String>(0)(); |
| } |
| ''', [ |
| error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 71, 6), |
| ]); |
| } |
| |
| test_extensionType_superBounded() async { |
| await assertErrorsInCode(''' |
| extension type A<T extends A<T>>(int it) {} |
| |
| void f(A a) {} |
| ''', [ |
| error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 52, 1, |
| contextMessages: [message(testFile, 52, 1)]), |
| ]); |
| } |
| |
| test_extensionType_withTypeArguments() async { |
| await assertErrorsInCode(''' |
| extension type A<T extends num>(int it) {} |
| |
| void f(A<String> a) {} |
| ''', [ |
| error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 53, 6), |
| ]); |
| } |
| |
| test_fieldFormalParameter() async { |
| await assertErrorsInCode(r''' |
| class A {} |
| class B {} |
| class G<E extends A> {} |
| class C { |
| var f; |
| C(G<B> this.f) {} |
| } |
| ''', [ |
| error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 71, 1, |
| contextMessages: [message(testFile, 69, 4)]), |
| ]); |
| } |
| |
| test_functionReference() async { |
| await assertErrorsInCode(''' |
| void foo<T extends num>(T a) {} |
| void bar() { |
| foo<String>; |
| } |
| ''', [ |
| error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 51, 6), |
| ]); |
| } |
| |
| test_functionReference_matching() async { |
| await assertNoErrorsInCode(''' |
| void foo<T extends num>(T a) {} |
| void bar() { |
| foo<int>; |
| } |
| '''); |
| } |
| |
| test_functionReference_regularBounded() async { |
| await assertNoErrorsInCode(''' |
| void foo<T>(T a) {} |
| void bar() { |
| foo<String>; |
| } |
| '''); |
| } |
| |
| test_functionReturnType() async { |
| await assertErrorsInCode(r''' |
| class A {} |
| class B {} |
| class G<E extends A> {} |
| G<B> f() => throw 0; |
| ''', [ |
| error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 48, 1, |
| contextMessages: [message(testFile, 46, 4)]), |
| ]); |
| } |
| |
| test_functionTypeAlias() async { |
| await assertErrorsInCode(r''' |
| class A {} |
| class B {} |
| class G<E extends A> {} |
| typedef G<B> f(); |
| ''', [ |
| error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 56, 1, |
| contextMessages: [message(testFile, 54, 4)]), |
| ]); |
| } |
| |
| test_functionTypedFormalParameter() async { |
| await assertErrorsInCode(r''' |
| class A {} |
| class B {} |
| class G<E extends A> {} |
| f(G<B> h()) {} |
| ''', [ |
| error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 50, 1, |
| contextMessages: [message(testFile, 48, 4)]), |
| ]); |
| } |
| |
| test_genericFunctionTypeArgument_invariant() async { |
| await assertErrorsInCode(r''' |
| typedef F = T Function<T>(T); |
| typedef FB<T extends F> = S Function<S extends T>(S); |
| class CB<T extends F> {} |
| void f(CB<FB<F>> a) {} |
| ''', [ |
| error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 119, 5, |
| contextMessages: [message(testFile, 116, 9)]), |
| ]); |
| } |
| |
| test_genericFunctionTypeArgument_regularBounded() async { |
| await assertNoErrorsInCode(r''' |
| typedef F1 = T Function<T>(T); |
| typedef F2 = S Function<S>(S); |
| class CB<T extends F1> {} |
| void f(CB<F2> a) {} |
| '''); |
| } |
| |
| test_implements() async { |
| await assertErrorsInCode(r''' |
| class A {} |
| class B {} |
| class G<E extends A> {} |
| class C implements G<B>{} |
| ''', [ |
| error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 67, 1), |
| ]); |
| } |
| |
| test_is() async { |
| await assertErrorsInCode(r''' |
| class A {} |
| class B {} |
| class G<E extends A> {} |
| var b = 1 is G<B>; |
| ''', [ |
| error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 61, 1, |
| contextMessages: [message(testFile, 59, 4)]), |
| ]); |
| } |
| |
| test_metadata_matching() async { |
| await assertNoErrorsInCode(r''' |
| class A<T extends num> { |
| const A(); |
| } |
| |
| @A<int>() |
| void f() {} |
| '''); |
| } |
| |
| test_metadata_notMatching() async { |
| await assertErrorsInCode(r''' |
| class A<T extends num> { |
| const A(); |
| } |
| |
| @A<String>() |
| void f() {} |
| ''', [ |
| error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 44, 6), |
| ]); |
| } |
| |
| test_metadata_notMatching_viaTypeAlias() async { |
| await assertErrorsInCode(r''' |
| class A<T> { |
| const A(); |
| } |
| |
| typedef B<T extends num> = A<T>; |
| |
| @B<String>() |
| void f() {} |
| ''', [ |
| error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 66, 6), |
| ]); |
| } |
| |
| test_methodInvocation_genericFunctionTypeArgument_match() async { |
| await assertNoErrorsInCode(r''' |
| typedef F = void Function<T extends num>(); |
| void f<T extends void Function<X extends num>()>() {} |
| void g() { |
| f<F>(); |
| } |
| '''); |
| } |
| |
| test_methodInvocation_genericFunctionTypeArgument_mismatch() async { |
| await assertErrorsInCode(r''' |
| class A {} |
| class B {} |
| typedef F = void Function<T extends A>(); |
| void f<T extends void Function<U extends B>()>() {} |
| void g() { |
| f<F>(); |
| } |
| ''', [ |
| error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 131, 1), |
| ]); |
| } |
| |
| test_methodInvocation_localFunction() async { |
| await assertErrorsInCode(r''' |
| class Point<T extends num> { |
| Point(T x, T y); |
| } |
| |
| main() { |
| Point<T> f<T extends num>(T x, T y) { |
| return new Point<T>(x, y); |
| } |
| print(f<String>('hello', 'world')); |
| } |
| ''', [ |
| error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 145, 6), |
| ]); |
| } |
| |
| test_methodInvocation_method() async { |
| await assertErrorsInCode(r''' |
| class Point<T extends num> { |
| Point(T x, T y); |
| } |
| |
| class PointFactory { |
| Point<T> point<T extends num>(T x, T y) { |
| return new Point<T>(x, y); |
| } |
| } |
| |
| f(PointFactory factory) { |
| print(factory.point<String>('hello', 'world')); |
| } |
| ''', [ |
| error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 202, 6), |
| ]); |
| } |
| |
| test_methodInvocation_topLevelFunction() async { |
| await assertErrorsInCode(r''' |
| class Point<T extends num> { |
| Point(T x, T y); |
| } |
| |
| Point<T> f<T extends num>(T x, T y) { |
| return new Point<T>(x, y); |
| } |
| |
| main() { |
| print(f<String>('hello', 'world')); |
| } |
| ''', [ |
| error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 140, 6), |
| ]); |
| } |
| |
| test_methodReturnType() async { |
| await assertErrorsInCode(r''' |
| class A {} |
| class B {} |
| class G<E extends A> {} |
| class C { |
| G<B> m() => throw 0; |
| } |
| ''', [ |
| error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 60, 1, |
| contextMessages: [message(testFile, 58, 4)]), |
| ]); |
| } |
| |
| test_new() async { |
| await assertErrorsInCode(r''' |
| class A {} |
| class B {} |
| class G<E extends A> {} |
| f() { return new G<B>(); } |
| ''', [ |
| error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 65, 1), |
| ]); |
| } |
| |
| test_new_matching() async { |
| await assertNoErrorsInCode(r''' |
| class A {} |
| class B extends A {} |
| class G<E extends A> {} |
| f() { return new G<B>(); } |
| '''); |
| } |
| |
| test_new_superTypeOfUpperBound() async { |
| await assertErrorsInCode(r''' |
| class A {} |
| class B extends A {} |
| class C extends B {} |
| class G<E extends B> {} |
| f() { return new G<A>(); } |
| ''', [ |
| error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 96, 1), |
| ]); |
| } |
| |
| test_nonFunctionTypeAlias_body_typeArgument_mismatch() async { |
| await assertErrorsInCode(r''' |
| class A {} |
| class B {} |
| class G<T extends A> {} |
| typedef X = G<B>; |
| ''', [ |
| error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 60, 1), |
| ]); |
| } |
| |
| test_nonFunctionTypeAlias_body_typeArgument_regularBounded() async { |
| await assertNoErrorsInCode(r''' |
| class A {} |
| class B extends A {} |
| class G<T extends A> {} |
| typedef X = G<B>; |
| '''); |
| } |
| |
| test_nonFunctionTypeAlias_body_typeArgument_superBounded() async { |
| await assertNoErrorsInCode(r''' |
| class A<T extends A<T>> {} |
| typedef X = List<A>; |
| '''); |
| } |
| |
| test_nonFunctionTypeAlias_interfaceType_body_mismatch() async { |
| await assertErrorsInCode(r''' |
| class A {} |
| class B {} |
| class G<T extends A> {} |
| typedef X = G<B>; |
| ''', [ |
| error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 60, 1), |
| ]); |
| } |
| |
| test_nonFunctionTypeAlias_interfaceType_body_regularBounded() async { |
| await assertNoErrorsInCode(r''' |
| class A<T> {} |
| typedef X<T> = A; |
| '''); |
| } |
| |
| test_nonFunctionTypeAlias_interfaceType_body_superBounded() async { |
| await assertErrorsInCode(r''' |
| class A<T extends A<T>> {} |
| typedef X<T> = A; |
| ''', [ |
| error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 42, 1, |
| contextMessages: [message(testFile, 42, 1)]), |
| ]); |
| } |
| |
| test_nonFunctionTypeAlias_interfaceType_parameter() async { |
| await assertErrorsInCode(r''' |
| class A {} |
| typedef X<T extends A> = Map<int, T>; |
| void f(X<String> a) {} |
| ''', [ |
| error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 58, 6, |
| contextMessages: [message(testFile, 56, 9)]), |
| ]); |
| } |
| |
| test_nonFunctionTypeAlias_interfaceType_parameter_regularBounded() async { |
| await assertNoErrorsInCode(r''' |
| class A {} |
| class B extends A {} |
| typedef X<T extends A> = Map<int, T>; |
| void f(X<B> a) {} |
| '''); |
| } |
| |
| Future<void> test_nonFunctionTypeAlias_parameter() async { |
| await assertErrorsInCode(''' |
| class A {} |
| class B extends A {} |
| class D<T> {} |
| typedef Alias<T extends B> = D<T>; |
| main() { |
| D d = Alias<A>(); |
| } |
| ''', [ |
| error(WarningCode.UNUSED_LOCAL_VARIABLE, 94, 1), |
| error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 104, 1), |
| ]); |
| } |
| |
| test_not_matching_bounds() async { |
| // There should be an error, because Bar's type argument T is Foo, which |
| // doesn't extends Foo<T>. |
| await assertErrorsInCode(''' |
| class Foo<T> {} |
| class Bar<T extends Foo<T>> {} |
| class Baz extends Bar {} |
| void main() {} |
| ''', [ |
| error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 65, 3, |
| contextMessages: [message(testFile, 65, 3)]), |
| ]); |
| // Instantiate-to-bounds should have instantiated "Bar" to "Bar<Foo>". |
| assertType(result.unit.declaredElement!.getClass('Baz')!.supertype, |
| 'Bar<Foo<dynamic>>'); |
| } |
| |
| test_notRegularBounded_notSuperBounded_parameter_invariant() async { |
| await assertErrorsInCode(r''' |
| typedef A<X> = X Function(X); |
| typedef G<X extends A<X>> = void Function<Y extends X>(); |
| foo(G g) {} |
| ''', [ |
| error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 92, 1, |
| contextMessages: [ |
| message(testFile, 92, 1), |
| message(testFile, 92, 1) |
| ]), |
| ]); |
| } |
| |
| test_ofFunctionTypeAlias() async { |
| await assertErrorsInCode(r''' |
| class A {} |
| class B {} |
| typedef F<T extends A>(); |
| F<B> fff = (throw 42); |
| ''', [ |
| error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 50, 1, |
| contextMessages: [message(testFile, 48, 4)]), |
| ]); |
| } |
| |
| test_ofFunctionTypeAlias_hasBound2_matching() async { |
| await assertNoErrorsInCode(r''' |
| class MyClass<T> {} |
| typedef MyFunction<T, P extends MyClass<T>>(); |
| class A<T, P extends MyClass<T>> { |
| MyFunction<T, P> f = (throw 0); |
| } |
| '''); |
| } |
| |
| test_ofFunctionTypeAlias_hasBound_matching() async { |
| await assertNoErrorsInCode(r''' |
| class A {} |
| class B extends A {} |
| typedef F<T extends A>(); |
| F<A> fa = (throw 0); |
| F<B> fb = (throw 0); |
| '''); |
| } |
| |
| test_ofFunctionTypeAlias_noBound_matching() async { |
| await assertNoErrorsInCode(r''' |
| typedef F<T>(); |
| F<int> f1 = (throw 0); |
| F<String> f2 = (throw 0); |
| '''); |
| } |
| |
| test_parameter() async { |
| await assertErrorsInCode(r''' |
| class A {} |
| class B {} |
| class G<E extends A> {} |
| f(G<B> g) {} |
| ''', [ |
| error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 50, 1, |
| contextMessages: [message(testFile, 48, 4)]), |
| ]); |
| } |
| |
| test_redirectingConstructor() async { |
| await assertErrorsInCode(r''' |
| class A {} |
| class B {} |
| class X<T extends A> { |
| X(int x, int y) {} |
| factory X.name(int x, int y) = X<B>; |
| } |
| ''', [ |
| error(CompileTimeErrorCode.REDIRECT_TO_INVALID_RETURN_TYPE, 99, 4), |
| error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 101, 1), |
| ]); |
| } |
| |
| test_regression_42196() async { |
| await assertNoErrorsInCode(r''' |
| typedef G<X> = Function(X); |
| class A<X extends G<A<X,Y>>, Y extends X> {} |
| |
| test<X>() { print("OK"); } |
| |
| main() { |
| test<A<G<A<Never, Never>>, dynamic>>(); |
| } |
| '''); |
| } |
| |
| test_regression_42196_object() async { |
| await assertNoErrorsInCode(r''' |
| typedef G<X> = Function(X); |
| class A<X extends G<A<X, Y>>, Y extends Never> {} |
| |
| test<X>() { print("OK"); } |
| |
| main() { |
| test<A<G<A<Never, Never>>, Object?>>(); |
| } |
| '''); |
| } |
| |
| test_regression_42196_void() async { |
| await assertNoErrorsInCode(r''' |
| typedef G<X> = Function(X); |
| class A<X extends G<A<X, Y>>, Y extends Never> {} |
| |
| test<X>() { print("OK"); } |
| |
| main() { |
| test<A<G<A<Never, Never>>, void>>(); |
| } |
| '''); |
| } |
| |
| test_superBounded() async { |
| await assertNoErrorsInCode(r''' |
| class A<X extends A<X>> {} |
| |
| A get foo => throw 0; |
| '''); |
| } |
| |
| test_typeArgumentList() async { |
| await assertErrorsInCode(r''' |
| class A {} |
| class B {} |
| class C<E> {} |
| class D<E extends A> {} |
| C<D<B>> c = (throw 0); |
| ''', [ |
| error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 64, 1, |
| contextMessages: [message(testFile, 62, 4)]), |
| ]); |
| } |
| |
| test_typeLiteral_class() async { |
| await assertErrorsInCode(''' |
| class C<T extends int> {} |
| var t = C<String>; |
| ''', [ |
| error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 36, 6, |
| contextMessages: [message(testFile, 34, 9)]), |
| ]); |
| } |
| |
| test_typeLiteral_functionTypeAlias() async { |
| await assertErrorsInCode(''' |
| typedef Cb<T extends int> = void Function(); |
| var t = Cb<String>; |
| ''', [ |
| error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 56, 6, |
| contextMessages: [message(testFile, 53, 10)]), |
| ]); |
| } |
| |
| test_typeLiteral_typeAlias() async { |
| await assertErrorsInCode(''' |
| class C {} |
| typedef D<T extends int> = C; |
| var t = D<String>; |
| ''', [ |
| error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 51, 6, |
| contextMessages: [message(testFile, 49, 9)]), |
| ]); |
| } |
| |
| test_typeParameter() async { |
| await assertErrorsInCode(r''' |
| class A {} |
| class B {} |
| class C {} |
| class G<E extends A> {} |
| class D<F extends G<B>> {} |
| ''', [ |
| error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 77, 1, |
| contextMessages: [message(testFile, 75, 4)]), |
| ]); |
| } |
| |
| test_variableDeclaration() async { |
| await assertErrorsInCode(r''' |
| class A {} |
| class B {} |
| class G<E extends A> {} |
| G<B> g = (throw 0); |
| ''', [ |
| error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 48, 1, |
| contextMessages: [message(testFile, 46, 4)]), |
| ]); |
| } |
| |
| test_with() async { |
| await assertErrorsInCode(r''' |
| class A {} |
| class B {} |
| mixin G<E extends A> {} |
| class C extends Object with G<B>{} |
| ''', [ |
| error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 76, 1), |
| ]); |
| } |
| } |