| // 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. |
| |
| // Tests the `toString` of a `Type` object for a type that is, |
| // or that contains, a `FutureOr` type, including whether its trailing `? s |
| // are correct. |
| // |
| // The `toString` is performed on the **NORM**-normalized type, |
| // and **NORM**(`FutureOr<T>?`), with `N` bein **NORM**(`T`), |
| // may be either `FutureOr<N>` or `FutureOr<N>?`, depending on |
| // whether `T`/`N` is nullable or not. |
| // |
| // This test checks that the `toString` result always matches |
| // the correctly normalized type format. |
| // Also checks for types `T` where normalization removes or changes |
| // the `FutureOr`. |
| |
| // Requirements=nnbd-strong |
| |
| import "dart:async" show FutureOr; |
| import "package:expect/expect.dart"; |
| |
| void main() { |
| // Generally assumes that `Type.toString()` mathches the canonical |
| // format of the type, as used in the language specification, |
| // with a leading name, `<...>` around type arguments, |
| // and trailing `?` for the nullable union types, |
| // and that it uses the same name for the same type every time, |
| // even if the names have been minified. |
| // |
| // That is the case for all current implementations, in all modes, |
| // even if `Type.toString()` doesn't document it. |
| |
| Match match = RegExp(r"^(.+)<(.+)<(.+)>>$") |
| .matchAsPrefix((A<FutureOr<String>>).toString())!; |
| String a = match[1]!; |
| String fo = match[2]!; |
| |
| Expect.equals("$String", match[3]); // Sanity check. |
| |
| void testType<T>(String expected) { |
| Expect.equals(expected, "$T"); |
| } |
| |
| void testRuntimeType(Object o, String expected) { |
| Expect.equals(expected, "${o.runtimeType}"); |
| } |
| |
| void testNullable<B>() { |
| assert(null is B); |
| var b = "$B"; |
| // Equal to `B` for top types and `Object`. |
| var fob = "${FutureOr<B>}"; |
| if (topOrObject<B>()) { |
| Expect.equals(b, fob); |
| } else if (isNull<B>()) { |
| Expect.equals("${Future<B>}?", fob); |
| } else { |
| Expect.equals("$fo<$b>", fob); |
| } |
| testType<FutureOr<B>>(fob); |
| testType<FutureOr<B>?>(fob); |
| testType<FutureOr<B?>>(fob); |
| testType<FutureOr<B?>?>(fob); |
| testType<FutureOr<N<B?>>>(fob); |
| testType<N<FutureOr<B>?>>(fob); |
| |
| var afob = "$a<$fob>"; |
| testType<A<FutureOr<B>?>>(afob); |
| testType<A<FutureOr<B?>>>(afob); |
| testType<A<FutureOr<B?>?>>(afob); |
| testType<A<FutureOr<N<B?>>>>(afob); |
| testType<A<N<FutureOr<B>?>>>(afob); |
| testRuntimeType(A<FutureOr<B>?>(), afob); |
| testRuntimeType(A<FutureOr<B?>>(), afob); |
| testRuntimeType(A<FutureOr<B?>?>(), afob); |
| testRuntimeType(A<FutureOr<N<B?>>>(), afob); |
| testRuntimeType(A<N<FutureOr<B>?>>(), afob); |
| } |
| |
| void testNotNullable<B>({bool alsoTestNullable = true}) { |
| assert(null is! B); |
| assert(!top<B>()); |
| var b = "$B"; |
| var fob = "${FutureOr<B>}"; |
| if (never<B>()) { |
| Expect.equals("${Future<B>}", fob); |
| } else if (topOrObject<B>()) { |
| // Object. |
| Expect.equals(b, fob); |
| } else { |
| Expect.equals("$fo<$b>", fob); |
| } |
| testType<FutureOr<B>>(fob); |
| testType<FutureOr<B>?>("$fob?"); |
| testType<N<FutureOr<B>?>>("$fob?"); |
| testType<A<FutureOr<B>?>>("$a<$fob?>"); |
| testType<A<N<FutureOr<B>?>>>("$a<$fob?>"); |
| testRuntimeType(A<FutureOr<B>?>(), "$a<$fob?>"); |
| testRuntimeType(A<N<FutureOr<B>?>>(), "$a<$fob?>"); |
| |
| if (alsoTestNullable) testNullable<B?>(); |
| } |
| |
| testNotNullable<String>(); |
| |
| testNullable<dynamic>(); |
| testNullable<void>(); |
| |
| testNotNullable<Object>(); |
| |
| testNullable<Null>(); |
| |
| testNotNullable<Never>(); |
| |
| // Some more cases that could be non-trivial, but really aren't. |
| testNotNullable<Future<Object>>(); |
| testNotNullable<Future<Object?>>(); |
| |
| // Test nested `FutureOr`, since normalization of that is somewhat |
| // non-trivial. |
| testNotNullable<FutureOr<String>>(alsoTestNullable: false); |
| testNullable<FutureOr<String>?>(); |
| testNullable<FutureOr<String?>>(); |
| testNullable<FutureOr<String?>?>(); |
| |
| // A singleton record with nullable field, for good measure. |
| testNotNullable<(String?,)>(); |
| } |
| |
| // Supports nesting nullables, like `N<N<N<int>>>`, which cannot |
| // syntactically be written as `int???`. |
| typedef N<T> = T?; |
| |
| bool top<T>() => <Object?>[] is List<T>; |
| bool topOrObject<T>() => <Object>[] is List<T>; |
| bool never<T>() => <T>[] is List<Never>; |
| bool isNull<T>() => <T>[] is List<Null> && null is T; |
| |
| class A<T> {} |