diff --git a/tests/language/closure/param_null_to_object_test.dart b/tests/language/closure/param_null_to_object_test.dart index e34f400670d..b1139f3a9c1 100644 --- a/tests/language/closure/param_null_to_object_test.dart +++ b/tests/language/closure/param_null_to_object_test.dart @@ -28,4 +28,39 @@ main() { Expect.isTrue(h is int Function(String)); Expect.isFalse(h is int Function(Object)); Expect.isFalse(h is int Function(Object?)); + + int Function(Null) i = (Null x) => 1; // Runtime type is int Function(Null) + Expect.isTrue(i is int Function(Null)); + Expect.isTrue(i is int Function(Never)); + Expect.isFalse(i is int Function(String)); + Expect.isFalse(i is int Function(Object)); + Expect.isFalse(i is int Function(Object?)); + + int Function(Never) j = (Never x) => 1; // Runtime type is int Function(Never) + Expect.isFalse(j is int Function(Null)); + Expect.isTrue(j is int Function(Never)); + Expect.isFalse(j is int Function(String)); + Expect.isFalse(j is int Function(Object)); + Expect.isFalse(j is int Function(Object?)); + + // Test that the criteria used for weakening the parameter type + // is that the downwards context parameter type is a subtype of Null + void test() { + int Function(X) f = (x) => 1; // Runtime type is int Function(Object?) + Expect.isTrue(f is int Function(Null)); + Expect.isTrue(f is int Function(Never)); + Expect.isTrue(f is int Function(String)); + Expect.isTrue(f is int Function(Object)); + Expect.isTrue(f is int Function(Object?)); + + int Function(Y) g = (x) => 1; // Runtime type is int Function(Object?) + Expect.isTrue(g is int Function(Null)); + Expect.isTrue(g is int Function(Never)); + Expect.isTrue(g is int Function(String)); + Expect.isTrue(g is int Function(Object)); + Expect.isTrue(g is int Function(Object?)); + } + + test(); + test(); } diff --git a/tests/language/nnbd/const/potentially_constant_types_error_test.dart b/tests/language/nnbd/const/potentially_constant_types_error_test.dart new file mode 100644 index 00000000000..02428360659 --- /dev/null +++ b/tests/language/nnbd/const/potentially_constant_types_error_test.dart @@ -0,0 +1,45 @@ +// Copyright (c) 2020, 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. + +// Requirements=nnbd-strong + +/// Test instance checks and casts in constants may use potentially constant +/// types, and cause compile time errors when the casts fail. + +class C1 { + final t; + + /// Check casts to T + const C1.test(dynamic x) : t = x as T; +} + +class C2 { + final l; + + /// Check casts to List + const C2.test(dynamic x) : l = x as List; +} + +void main() { + const c1 = C1.test("hello"); + // ^ + // [analyzer] unspecified + // [cfe] Constant evaluation error: + const c2 = C1.test(null); + // ^ + // [analyzer] unspecified + // [cfe] unspecified + const c3 = C2.test([0]); + // ^ + // [analyzer] unspecified + // [cfe] Constant evaluation error: + const c4 = C2.test("hello"); + // ^ + // [analyzer] unspecified + // [cfe] Constant evaluation error: + const c5 = C2.test(null); + // ^ + // [analyzer] unspecified + // [cfe] unspecified +} diff --git a/tests/language/nnbd/const/potentially_constant_types_test.dart b/tests/language/nnbd/const/potentially_constant_types_test.dart new file mode 100644 index 00000000000..7e45aeb0724 --- /dev/null +++ b/tests/language/nnbd/const/potentially_constant_types_test.dart @@ -0,0 +1,143 @@ +// Copyright (c) 2020, 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. + +// Requirements=nnbd-strong + +/// Test instance checks and casts in constants may use potentially constant +/// types, and evaluate appropriately. + +import 'package:expect/expect.dart'; + +class C { + final bool isT; + final bool isListT; + final T? t; + final List? l; + + /// Check instance tests in isolation + const C.test1(dynamic x) + : isT = x is T, + isListT = x is List, + t = null, + l = null; + + /// Check casts to T in isolation + const C.test2(dynamic x) + : isT = true, + isListT = false, + t = x as T, + l = null; + + /// Check casts to List in isolation + const C.test3(dynamic x) + : isT = false, + isListT = true, + t = null, + l = x as List; + + /// Combine instance checks with casts, conditional expressions, promotion + const C.test4(dynamic x) + : isT = x is T, + isListT = x is List, + t = (x is T) ? x : null, + l = (x is List) ? x : null; +} + +void main() { + { + // Test instance checks of T + const c1 = C.test1(0); + const c2 = C.test1(0); + const c3 = C.test1(1); + const c4 = C.test1("hello"); + const c5 = C.test1(null); + Expect.identical(c1, c2); + Expect.identical(c1, c3); + Expect.notIdentical(c1, c4); + Expect.notIdentical(c1, c5); + Expect.isTrue(c1.isT); + Expect.isTrue(c2.isT); + Expect.isTrue(c3.isT); + Expect.isFalse(c4.isT); + Expect.isFalse(c5.isT); + Expect.isFalse(c1.isListT); + Expect.isFalse(c2.isListT); + Expect.isFalse(c3.isListT); + Expect.isFalse(c4.isListT); + Expect.isFalse(c5.isListT); + } + { + // Test instance checks of List + const c1 = C.test1([0]); + const c2 = C.test1([0]); + const c3 = C.test1([1]); + const c4 = C.test1([1]); + const c5 = C.test1([1]); + Expect.identical(c1, c2); + Expect.identical(c1, c3); + Expect.notIdentical(c1, c4); + Expect.notIdentical(c1, c5); + Expect.notIdentical(c4, c5); + Expect.isFalse(c1.isT); + Expect.isFalse(c2.isT); + Expect.isFalse(c3.isT); + Expect.isFalse(c4.isT); + Expect.isFalse(c5.isT); + Expect.isTrue(c1.isListT); + Expect.isTrue(c2.isListT); + Expect.isTrue(c3.isListT); + Expect.isFalse(c4.isListT); + Expect.isTrue(c5.isListT); + } + { + // Test casts to T + const c1 = C.test2(0); + const c2 = C.test2(0); + const c3 = C.test2(1); + Expect.identical(c1, c2); + Expect.notIdentical(c1, c3); + } + { + // Test casts to List + const c1 = C.test3([0]); + const c2 = C.test3([0]); + const c3 = C.test3([0]); + Expect.identical(c1, c2); + Expect.notIdentical(c1, c3); + } + + { + // Combined tests + const c1 = C.test4(0); + const c2 = C.test4("hello"); + const c3 = C.test4([0]); + const c4 = C.test4(["hello"]); + const c5 = C.test4([0]); + + Expect.isTrue(c1.isT); + Expect.isFalse(c1.isListT); + Expect.equals(c1.t, 0); + Expect.equals(c1.l, null); + + Expect.isFalse(c2.isT); + Expect.isFalse(c2.isListT); + Expect.equals(c2.t, null); + Expect.equals(c2.l, null); + + Expect.isFalse(c3.isT); + Expect.isTrue(c3.isListT); + Expect.equals(c3.t, null); + Expect.identical(c3.l, const [0]); + + Expect.isFalse(c4.isT); + Expect.isFalse(c4.isListT); + Expect.equals(c4.t, null); + Expect.equals(c4.l, null); + + Expect.isFalse(c5.isT); + Expect.isFalse(c5.isListT); + Expect.equals(c5.t, null); + Expect.equals(c5.l, null); + } +} diff --git a/tests/language/nnbd/never/never_error_test.dart b/tests/language/nnbd/never/never_error_test.dart new file mode 100644 index 00000000000..9ce76097145 --- /dev/null +++ b/tests/language/nnbd/never/never_error_test.dart @@ -0,0 +1,126 @@ +// Copyright (c) 2020, 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. + +// Requirements=nnbd-strong + +/// Test that `Never` has all members, and that as a result no extensions +/// apply to it. Also test that `Never` is inferred as the type of a +/// throw expression, and as the return type of function literals that +/// never return. + +import 'package:expect/expect.dart'; + +/// A call of the form staticErrorIfNotNever(t) will produce a static error +/// unless `t` has type `Never`. +void staticErrorIfNotNever(T t) {} + +void neverHasAllMembers(Never x) { + { + // getters + var t = x.arglebargle; + staticErrorIfNotNever(t); + } + { + // setter + x.arglebargle = 3; + } + { + // operator[] + x[0] = 3; + } + { + // methods + var t = x.arglebargle(0); + staticErrorIfNotNever(t); + } + { + // methods with named parameters + var t = x.arglebargle(foo: 0); + staticErrorIfNotNever(t); + } + { + // call method + var t = x(3); + staticErrorIfNotNever(t); + } + { + // Object members + staticErrorIfNotNever(x.toString()); + staticErrorIfNotNever(x.toString); + staticErrorIfNotNever(x.runtimeType); + staticErrorIfNotNever(x.noSuchMethod); + staticErrorIfNotNever(x.noSuchMethod(x)); + staticErrorIfNotNever(x.hashCode); + staticErrorIfNotNever(x == x); + staticErrorIfNotNever(x == 3); + staticErrorIfNotNever(3 == x); +// ^^^^^^^^^^^^^^^^^ +// [analyzer] COMPILE_TIME_ERROR.COULD_NOT_INFER +// [cfe] Inferred type argument 'bool' doesn't conform to the bound 'Never' of the type variable 'T' on 'staticErrorIfNotNever'. + } +} + +extension NeverExt on Never { + int neverMethod() => 3; +} + +extension ObjectExt on Object { + int objectMethod() => 3; +} + +void extensionsDontApply(Never x) { + { + var t = x.neverMethod(); + staticErrorIfNotNever(t); + } + { + var t = x.objectMethod(); + staticErrorIfNotNever(t); + } + { + var t = NeverExt(x).neverMethod(); + staticErrorIfNotNever(t); +// ^^^^^^^^^^^^^^^^^ +// [analyzer] COMPILE_TIME_ERROR.COULD_NOT_INFER +// [cfe] Inferred type argument 'int' doesn't conform to the bound 'Never' of the type variable 'T' on 'staticErrorIfNotNever'. + } + { + var t = ObjectExt(x).objectMethod(); + staticErrorIfNotNever(t); +// ^^^^^^^^^^^^^^^^^ +// [analyzer] COMPILE_TIME_ERROR.COULD_NOT_INFER +// [cfe] Inferred type argument 'int' doesn't conform to the bound 'Never' of the type variable 'T' on 'staticErrorIfNotNever'. + } +} + +void throwHasTypeNever() { + var t = throw "hello"; + staticErrorIfNotNever(t); +} + +void neverReturns(bool b) { + { + var f = () => throw "Unreachable"; + var t = f(); + staticErrorIfNotNever(t); + } + { + var f = () { + if (b) { + throw "argle"; + } else { + throw "bargle"; + } + }; + var t = f(); + staticErrorIfNotNever(t); + } +} + +void main() { + neverHasAllMembers(throw "Unreachable"); + extensionsDontApply(throw "Unreachable"); + throwHasTypeNever(); + neverReturns(true); +} diff --git a/tests/language/nnbd/static_errors/local_function_inference.dart b/tests/language/nnbd/static_errors/local_function_inference.dart new file mode 100644 index 00000000000..9cb6a37b150 --- /dev/null +++ b/tests/language/nnbd/static_errors/local_function_inference.dart @@ -0,0 +1,29 @@ +// Copyright (c) 2020, 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. + +/// Test that the type of a local function is an error if local function type +/// inference requires the type of the function being inferred. + +void main() { + f() { + return 3; + } + + f().arglebargle; + // ^^^^^^^^^^^ + // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER + // [cfe] The getter 'arglebargle' isn't defined for the class 'int'. + f().isEven; // Inferred type is int + + g() { + if (f() == 3) { + return g(); + } else { + return 3; + } + } + // ^ + // [analyzer] undefined + // [cfe] undefined +} diff --git a/tests/language/nnbd/type_promotion/downwards_inference_error_test.dart b/tests/language/nnbd/type_promotion/downwards_inference_error_test.dart new file mode 100644 index 00000000000..d54b522ccf2 --- /dev/null +++ b/tests/language/nnbd/type_promotion/downwards_inference_error_test.dart @@ -0,0 +1,152 @@ +// Copyright (c) 2020, 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. + +/// Test that downwards inference uses the promoted type of the variable in an +/// assignment as the context, but still considers the assignment as a valid +/// promotion/demotion point. + +/// A generic class to serve as the base type. +class C { + S cMethod(S x) => x; +} + +/// An inference context C constrains the first type variable of D but not +/// the second. +class D extends C { + S dMethod1(S x) => x; + T dMethod2(T x) => x; +} + +/// Generic function which if inferred in a context C with argument type B, +/// should infer to mkD +D mkD(T x) => D(); + +/// Generic function which if inferred in a context D with argument type +/// D, should infer to useD. +C useD(D d) => d; + +void main() { + { + C x = C(); + + // Inference uses C as a downwards context, constraining S from + // mkD to String. Upwards inference constrains T to int. D is not a type of interest so no promotion should happen. + { + // y has the type of the RHS of the assignment + var y = x = mkD(3); + + // x still has type C + x.dMethod1("hello"); + //^^^^^^^^ + // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_METHOD + // [cfe] The method 'dMethod1' isn't defined for the class 'C'. + x.dMethod2(3); + //^^^^^^^^ + // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_METHOD + // [cfe] The method 'dMethod2' isn't defined for the class 'C'. + + var t0 = x.cMethod("hello"); + t0.length; + t0.arglebargle; // t0 is not dynamic + // ^^^^^^^^^^^ + // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER + // [cfe] The getter 'arglebargle' isn't defined for the class 'String'. + + // y has type D + var t1 = y.dMethod1("hello"); + t1.length; + t1.arglebargle; // t1 is not dynamic + // ^^^^^^^^^^^ + // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER + // [cfe] The getter 'arglebargle' isn't defined for the class 'String'. + var t2 = y.dMethod2(3); + t2.isEven; + t2.arglebargle; // t2 is not dynamic + // ^^^^^^^^^^^ + // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER + // [cfe] The getter 'arglebargle' isn't defined for the class 'int'. + } + + // Establish D as a type of interest + if (x is D) {} + // Inference uses C as a downwards context, constraining S from + // mkD to String. Upwards inference constrains T to int. D is a type of interest so promotion should happen. + { + // y has the type of the RHS of the assignment + var y = x = mkD(3); + + // x has type D + var t0 = x.dMethod1("hello"); + t0.length; + t0.arglebargle; // t0 is not dynamic + // ^^^^^^^^^^^ + // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER + // [cfe] The getter 'arglebargle' isn't defined for the class 'String'. + var t1 = x.dMethod2(3); + t1.isEven; + t1.arglebargle; // t1 is not dynamic + // ^^^^^^^^^^^ + // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER + // [cfe] The getter 'arglebargle' isn't defined for the class 'int'. + + // y has type D + var t2 = y.dMethod1("hello"); + t2.length; + t2.arglebargle; // t2 is not dynamic + // ^^^^^^^^^^^ + // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER + // [cfe] The getter 'arglebargle' isn't defined for the class 'String'. + var t3 = y.dMethod2(3); + t3.isEven; + t3.arglebargle; // t3 is not dynamic + // ^^^^^^^^^^^ + // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER + // [cfe] The getter 'arglebargle' isn't defined for the class 'int'. + } + + // Inference should use D as a downwards context, T from + // useD to int. The variable x as an argument has type D + // which is assignable to D so the call should have no error. + // C is a type of interest, so x should be demoted after the call. + { + // y has the type of the RHS of the assignment + var y = x = useD(x); + + // x has type C, and not D + x.dMethod1("hello"); + //^^^^^^^^ + // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_METHOD + // [cfe] The method 'dMethod1' isn't defined for the class 'C'. + x.dMethod2(3); + //^^^^^^^^ + // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_METHOD + // [cfe] The method 'dMethod2' isn't defined for the class 'C'. + + var t0 = x.cMethod("hello"); + t0.length; + t0.arglebargle; // t0 is not dynamic + // ^^^^^^^^^^^ + // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER + // [cfe] The getter 'arglebargle' isn't defined for the class 'String'. + + // C, and not D + y.dMethod1("hello"); + //^^^^^^^^ + // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_METHOD + // [cfe] The method 'dMethod1' isn't defined for the class 'C'. + y.dMethod2(3); + //^^^^^^^^ + // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_METHOD + // [cfe] The method 'dMethod2' isn't defined for the class 'C'. + var t1 = y.cMethod("hello"); + t1.length; + t1.arglebargle; // t0 is not dynamic + // ^^^^^^^^^^^ + // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER + // [cfe] The getter 'arglebargle' isn't defined for the class 'String'. + } + } +} diff --git a/tests/language/nnbd/typedef/typedef_error_test.dart b/tests/language/nnbd/typedef/typedef_error_test.dart new file mode 100644 index 00000000000..f1bed377d69 --- /dev/null +++ b/tests/language/nnbd/typedef/typedef_error_test.dart @@ -0,0 +1,21 @@ +// Copyright (c) 2020, 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. + +// Requirements=nnbd-weak + +/// Test that typedefs imported from opted out libraries are treated as +/// non-nullable at the top level, with legacy components. + +import 'typedef_opted_out.dart'; + +int? takesNonNullable(int x) {} +void main() { + F f = null; // typedefs from opted out libraries are treated as non-nullable + // ^^^^ + // [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT + // [cfe] A value of type 'Null' can't be assigned to a variable of type 'int Function(int)'. + + f = takesNonNullable; // F is int* Function(int*) + f(null); // F is int* Function(int*) +} diff --git a/tests/language/nnbd/typedef/typedef_opted_out.dart b/tests/language/nnbd/typedef/typedef_opted_out.dart new file mode 100644 index 00000000000..1576bc620ce --- /dev/null +++ b/tests/language/nnbd/typedef/typedef_opted_out.dart @@ -0,0 +1,7 @@ +// Copyright (c) 2020, 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. + +// @dart=2.7 + +typedef F = int Function(int);