Attention is currently required from: Jacob Richman.
Lasse R.H. Nielsen uploaded patch set #6 to this change.
Add `unawaited` function and `ignore` extensions member.
The `unawaited` function in `dart:async` is intended for use with the `unawaited_futures` lint which is hopefully going to be part of the Dart recommended set of lints.
The `ignore` extension method is there to provide an alternative if you even want to ignore errors from a future. By having both, it makes the distinction clearer and makes it easier to not think one can be used for everything.
Change-Id: Ib96ed5ff64ead4b228721e5210efa82f76119c9f
---
M CHANGELOG.md
M sdk/lib/async/future.dart
M sdk/lib/async/future_impl.dart
M tests/language/static_type_helper.dart
A tests/language_2/static_type_helper.dart
A tests/lib/async/future_extension_test.dart
A tests/lib/async/unawaited_error_test.dart
A tests/lib/async/unawaited_test.dart
A tests/lib_2/async/future_extension_test.dart
A tests/lib_2/async/unawaited_error_test.dart
A tests/lib_2/async/unawaited_test.dart
11 files changed, 454 insertions(+), 25 deletions(-)
To view, visit change 200428. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Jacob Richman.
go/dart-cbuild result: SUCCESS
Details: https://goto.google.com/dart-cbuild/find/b09cfbf466af6e5645803ce0d2e6c567934586cf
Attention is currently required from: Jacob Richman.
go/dart-cbuild result: SUCCESS
Details: https://goto.google.com/dart-cbuild/find/09ebde8e9d831060fa640650ba13550e127fa787
Attention is currently required from: Jacob Richman.
go/dart-cbuild result: SUCCESS
Details: https://goto.google.com/dart-cbuild/find/f99ce00d6ab51dfa74ffc21943d8df33ec70d2cd
Attention is currently required from: Lasse R.H. Nielsen.
Patch set 9:Code-Review +1
1 comment:
Patchset:
PTAL @nbosch too.
Has changed `unawaited` to a top-level function in `dart:async`. It's not exported from `dart:core`.
To view, visit change 200428. To unsubscribe, or for help writing mail filters, visit settings.
Patch set 9:Commit-Queue +2
Attention is currently required from: Lasse R.H. Nielsen.
Patch set 9:Code-Review +1
commi...@chromium.org submitted this change.
Add `unawaited` function and `ignore` extensions member.
The `unawaited` function in `dart:async` is intended for use with the `unawaited_futures` lint which is hopefully going to be part of the Dart recommended set of lints.
The `ignore` extension method is there to provide an alternative if you even want to ignore errors from a future. By having both, it makes the distinction clearer and makes it easier to not think one can be used for everything.
Change-Id: Ib96ed5ff64ead4b228721e5210efa82f76119c9f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/200428
Reviewed-by: Jacob Richman <jac...@google.com>
Reviewed-by: Nate Bosch <nbo...@google.com>
Commit-Queue: Lasse R.H. Nielsen <l...@google.com>
---
M CHANGELOG.md
M sdk/lib/async/future.dart
M sdk/lib/async/future_impl.dart
M tests/language/static_type_helper.dart
A tests/language_2/static_type_helper.dart
A tests/lib/async/future_extension_test.dart
A tests/lib/async/unawaited_error_test.dart
A tests/lib/async/unawaited_test.dart
A tests/lib_2/async/future_extension_test.dart
A tests/lib_2/async/unawaited_error_test.dart
A tests/lib_2/async/unawaited_test.dart
11 files changed, 457 insertions(+), 25 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4b4cef9..7d5d4ba3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,9 +12,14 @@
#### `dart:async`
-* The uncaught error handlers of `Zone`s are now run in the parent zone
- of the zone where they were declared. This prevents a throwing handler
- from causing an infinite loop by repeatedly triggering itself.
+* The uncaught error handlers of `Zone`s are now run in the parent zone
+ of the zone where they were declared. This prevents a throwing handler
+ from causing an infinite loop by repeatedly triggering itself.
+
+* Added `ignore()` as extension member on futures.
+
+* Added `void unawaited(Future)` top-level function to deal with
+ the `unawaited_futures` lint.
#### `dart:core`
diff --git a/sdk/lib/async/future.dart b/sdk/lib/async/future.dart
index 4d98a53..edc6264 100644
--- a/sdk/lib/async/future.dart
+++ b/sdk/lib/async/future.dart
@@ -698,6 +698,30 @@
Future<T> timeout(Duration timeLimit, {FutureOr<T> onTimeout()?});
}
+/// Explicitly ignores a future.
+///
+/// Not all futures need to be awaited.
+/// The Dart linter has an optional ["unawaited futures" lint](https://dart-lang.github.io/linter/lints/unawaited_futures.html)
+/// which enforces that futures (expressions with a static type of [Future])
+/// in asynchronous functions are handled *somehow*.
+/// If a particular future value doesn't need to be awaited,
+/// you can call `unawaited(...)` with it, which will avoid the lint,
+/// simply because the expression no longer has type [Future].
+/// Using `unawaited` has no other effect.
+/// You should use `unawaited` to convey the *intention* of
+/// deliberately not waiting for the future.
+///
+/// If the future completes with an error,
+/// it was likely a mistake to not await it.
+/// That error will still occur and will be considered unhandled
+/// unless the same future is awaited (or otherwise handled) elsewhere too.
+/// Because of that, `unawaited` should only be used for futures that
+/// are *expected* to complete with a value.
+/// You can use [FutureExtension.ignore] if you also don't want to know
+/// about errors from this future.
+@Since("2.15")
+void unawaited(Future<void> future) {}
+
/// Convenience methods on futures.
///
/// Adds functionality to futures which makes it easier to
@@ -770,6 +794,32 @@
handleError(error as E, stackTrace),
test: (Object error) => error is E && (test == null || test(error)));
}
+
+ /// Completely ignores this future and its result.
+ ///
+ /// Not all futures are important, not even if they contain errors,
+ /// for example if a request was made, but the response is no longer needed.
+ /// Simply ignoring a future can result in uncaught asynchronous errors.
+ /// This method instead handles (and ignores) any values or errors
+ /// coming from this future, making it safe to otherwise ignore
+ /// the future.
+ ///
+ /// Use `ignore` to signal that the result of the future is
+ /// no longer important to the program, not even if it's an error.
+ /// If you merely want to silence the ["unawaited futures" lint](https://dart-lang.github.io/linter/lints/unawaited_futures.html),
+ /// use the [unawaited] function instead.
+ /// That will ensure that an unexpected error is still reported.
+ @Since("2.15")
+ void ignore() {
+ var self = this;
+ if (self is _Future<T>) {
+ self._ignore();
+ } else {
+ self.then<void>(_ignore, onError: _ignore);
+ }
+ }
+
+ static void _ignore(Object? _, [Object? __]) {}
}
/// Thrown when a scheduled timeout happens while waiting for an async result.
diff --git a/sdk/lib/async/future_impl.dart b/sdk/lib/async/future_impl.dart
index e73300f..c89d345 100644
--- a/sdk/lib/async/future_impl.dart
+++ b/sdk/lib/async/future_impl.dart
@@ -69,9 +69,14 @@
static const int maskTestError = 4;
static const int maskWhenComplete = 8;
static const int stateChain = 0;
+ // Handles values, passes errors on.
static const int stateThen = maskValue;
+ // Handles values and errors.
static const int stateThenOnerror = maskValue | maskError;
+ // Handles errors, has errorCallback.
static const int stateCatchError = maskError;
+ // Ignores both values and errors. Has no callback or errorCallback.
+ // The [result] future is ignored, its always the same as the source.
static const int stateCatchErrorTest = maskError | maskTestError;
static const int stateWhenComplete = maskWhenComplete;
static const int maskType =
@@ -191,21 +196,40 @@
/// [_FutureListener] listeners.
static const int _stateIncomplete = 0;
+ /// Flag set when an error need not be handled.
+ ///
+ /// Set by the [FutureExtensions.ignore] method to avoid
+ /// having to introduce an unnecessary listener.
+ /// Only relevant until the future is completed.
+ static const int _stateIgnoreError = 1;
+
/// Pending completion. Set when completed using [_asyncComplete] or
/// [_asyncCompleteError]. It is an error to try to complete it again.
/// [_resultOrListeners] holds listeners.
- static const int _statePendingComplete = 1;
+ static const int _statePendingComplete = 2;
- /// The future has been chained to another future. The result of that
- /// other future becomes the result of this future as well.
+ /// The future has been chained to another "source" [_Future].
+ ///
+ /// The result of that other future becomes the result of this future
+ /// as well, when the other future completes.
+ /// This future cannot be completed again.
/// [_resultOrListeners] contains the source future.
- static const int _stateChained = 2;
+ /// Listeners have been moved to the chained future.
+ static const int _stateChained = 4;
/// The future has been completed with a value result.
- static const int _stateValue = 4;
+ ///
+ /// [_resultOrListeners] contains the value.
+ static const int _stateValue = 8;
/// The future has been completed with an error result.
- static const int _stateError = 8;
+ ///
+ /// [_resultOrListeners] contains an [AsyncEror]
+ /// holding the error and stack trace.
+ static const int _stateError = 16;
+
+ /// Mask for the states above except [_stateIgnoreError].
+ static const int _completionStateMask = 30;
/// Whether the future is complete, and as what.
int _state = _stateIncomplete;
@@ -227,8 +251,8 @@
/// and it is not chained to another future.
///
/// The future is another future that this future is chained to. This future
- /// is waiting for the other future to complete, and when it does, this future
- /// will complete with the same result.
+ /// is waiting for the other future to complete, and when it does,
+ /// this future will complete with the same result.
/// All listeners are forwarded to the other future.
@pragma("vm:entry-point")
var _resultOrListeners;
@@ -253,12 +277,14 @@
/// Creates a future that is already completed with the value.
_Future.value(T value) : this.zoneValue(value, Zone._current);
- bool get _mayComplete => _state == _stateIncomplete;
- bool get _isPendingComplete => _state == _statePendingComplete;
- bool get _mayAddListener => _state <= _statePendingComplete;
- bool get _isChained => _state == _stateChained;
- bool get _isComplete => _state >= _stateValue;
- bool get _hasError => _state == _stateError;
+ bool get _mayComplete => (_state & _completionStateMask) == _stateIncomplete;
+ bool get _isPendingComplete => (_state & _statePendingComplete) != 0;
+ bool get _mayAddListener =>
+ _state <= (_statePendingComplete | _stateIgnoreError);
+ bool get _isChained => (_state & _stateChained) != 0;
+ bool get _isComplete => (_state & (_stateValue | _stateError)) != 0;
+ bool get _hasError => (_state & _stateError) != 0;
+ bool get _ignoreError => (_state & _stateIgnoreError) != 0;
static List<Function>? _continuationFunctions(_Future<Object> future) {
List<Function>? result = null;
@@ -283,7 +309,7 @@
void _setChained(_Future source) {
assert(_mayAddListener);
- _state = _stateChained;
+ _state = _stateChained | (_state & _stateIgnoreError);
_resultOrListeners = source;
}
@@ -315,6 +341,10 @@
return result;
}
+ void _ignore() {
+ _state |= _stateIgnoreError;
+ }
+
Future<T> catchError(Function onError, {bool test(Object error)?}) {
_Future<T> result = new _Future<T>();
if (!identical(result._zone, _rootZone)) {
@@ -337,13 +367,13 @@
Stream<T> asStream() => new Stream<T>.fromFuture(this);
void _setPendingComplete() {
- assert(_mayComplete);
- _state = _statePendingComplete;
+ assert(_mayComplete); // Aka _statIncomplete
+ _state ^= _stateIncomplete ^ _statePendingComplete;
}
void _clearPendingComplete() {
assert(_isPendingComplete);
- _state = _stateIncomplete;
+ _state ^= _statePendingComplete ^ _stateIncomplete;
}
AsyncError get _error {
@@ -365,7 +395,7 @@
void _setErrorObject(AsyncError error) {
assert(!_isComplete); // But may have a completion pending.
- _state = _stateError;
+ _state = _stateError | (_state & _stateIgnoreError);
_resultOrListeners = error;
}
@@ -379,7 +409,8 @@
void _cloneResult(_Future source) {
assert(!_isComplete);
assert(source._isComplete);
- _state = source._state;
+ _state =
+ (source._state & _completionStateMask) | (_state & _stateIgnoreError);
_resultOrListeners = source._resultOrListeners;
}
@@ -615,7 +646,7 @@
assert(source._isComplete);
bool hasError = source._hasError;
if (listeners == null) {
- if (hasError) {
+ if (hasError && !source._ignoreError) {
AsyncError asyncError = source._error;
source._zone
.handleUncaughtError(asyncError.error, asyncError.stackTrace);
diff --git a/tests/language/static_type_helper.dart b/tests/language/static_type_helper.dart
index 9484387..4a7809a 100644
--- a/tests/language/static_type_helper.dart
+++ b/tests/language/static_type_helper.dart
@@ -2,7 +2,10 @@
// 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.
-// Ensures a context type of [T] for the operand.
+/// Helper to create [Type] values.
+Type typeOf<T>() => T;
+
+/// Ensures a context type of [T] for the operand.
void context<T>(T x) {}
/// Captures the context type of the call and returns the same type.
@@ -34,6 +37,23 @@
T expectStaticType<R extends Exactly<T>>() {
return this;
}
+
+ /// Invokes [callback] with the static type of `this`.
+ ///
+ /// Allows any operation on the type.
+ T captureStaticType(void Function<X>() callback) {
+ callback<T>();
+ return this;
+ }
+}
+
+/// Invokes [callback] with the static type of [value].
+///
+/// Similar to [StaticType.captureStaticType], but works
+/// for types like `void` and `dynamic` which do not allow
+/// extension methods.
+void captureStaticType<T>(T value, void Function<X>(X value) callback) {
+ callback<T>(value);
}
/// Use with [StaticType.expectStaticType] to expect precisely the type [T].
diff --git a/tests/language_2/static_type_helper.dart b/tests/language_2/static_type_helper.dart
new file mode 100644
index 0000000..4a7809a
--- /dev/null
+++ b/tests/language_2/static_type_helper.dart
@@ -0,0 +1,90 @@
+// 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.
+
+/// Helper to create [Type] values.
+Type typeOf<T>() => T;
+
+/// Ensures a context type of [T] for the operand.
+void context<T>(T x) {}
+
+/// Captures the context type of the call and returns the same type.
+///
+/// Can be used to check the context type as:
+/// ```dart
+/// int x = contextType(1 /* valid value */)..expectStaticType<Exactly<int>>;
+/// ```
+T contextType<T>(Object result) => result as T;
+
+extension StaticType<T> on T {
+ /// Check the static type.
+ ///
+ /// Use as follows (assuming `e` has static type `num`):
+ /// ```dart
+ /// e.expectStaticType<Exactly<num>>() // No context type.
+ /// e.expectStaticType<SubtypeOf<Object>>() // No context type.
+ /// e.expectStaticType<SupertypeOf<int>>() // No context type.
+ /// ```
+ /// or
+ /// ```dart
+ /// e..expectStaticType<Exactly<num>>() // Preserve context type.
+ /// e..expectStaticType<SubtypeOf<Object>>() // Preserve context type.
+ /// e..expectStaticType<SupertypeOf<int>>() // Preserve context type.
+ /// ```
+ /// This will be a *compile-time error* if the static type is not
+ /// as required by the constraints type (the one passed to [Exactly],
+ /// [SubtypeOf] or [SupertypeOf].)
+ T expectStaticType<R extends Exactly<T>>() {
+ return this;
+ }
+
+ /// Invokes [callback] with the static type of `this`.
+ ///
+ /// Allows any operation on the type.
+ T captureStaticType(void Function<X>() callback) {
+ callback<T>();
+ return this;
+ }
+}
+
+/// Invokes [callback] with the static type of [value].
+///
+/// Similar to [StaticType.captureStaticType], but works
+/// for types like `void` and `dynamic` which do not allow
+/// extension methods.
+void captureStaticType<T>(T value, void Function<X>(X value) callback) {
+ callback<T>(value);
+}
+
+/// Use with [StaticType.expectStaticType] to expect precisely the type [T].
+///
+/// Example use:
+/// ```dart
+/// "abc".expectStaticType<Exactly<String>>();
+/// ```
+typedef Exactly<T> = T Function(T);
+
+/// Use with [StaticType.expectStaticType] to expect a subtype of [T].
+///
+/// Example use:
+/// ```dart
+/// num x = 1;
+/// x.expectStaticType<SubtypeOf<Object>>();
+/// ```
+typedef SubtypeOf<T> = Never Function(T);
+
+/// Use with [StaticType.expectStaticType] to expect a supertype of [T].
+///
+/// Example use:
+/// ```dart
+/// num x = 1;
+/// x.expectStaticType<SupertypeOf<int>>();
+/// ```
+typedef SupertypeOf<T> = T Function(Object?);
+
+/// Checks that an expression is assignable to [T1], [T2] and [Object].
+///
+/// This ensures that the static type of the expression is either dynamic,
+/// Never, or a type assignable to both [T1] and [T2], and if those are
+/// unrelated, it must be an intersection type.
+void checkIntersectionType<T1, T2>(T1 v1, T2 v2, Object v3) {}
diff --git a/tests/lib/async/future_extension_test.dart b/tests/lib/async/future_extension_test.dart
new file mode 100644
index 0000000..cd28986
--- /dev/null
+++ b/tests/lib/async/future_extension_test.dart
@@ -0,0 +1,54 @@
+// Copyright (c) 2021, 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:async_helper/async_helper.dart';
+import "package:expect/expect.dart";
+import 'dart:async' show Completer, runZonedGuarded;
+import '../../language/static_type_helper.dart';
+
+void main() {
+ testIgnore();
+}
+
+void testIgnore() {
+ var future = Future<int>.value(42);
+ captureStaticType(future.ignore(), <T>(T value) {
+ Expect.equals(typeOf<void>(), T);
+ });
+
+ asyncStart();
+ // Ignored futures can still be listend to.
+ {
+ var c = Completer<int>.sync();
+ var f = c.future;
+ f.ignore();
+ asyncStart();
+ f.catchError((e) {
+ Expect.equals("ERROR1", e);
+ asyncEnd();
+ return 0;
+ });
+ c.completeError("ERROR1");
+ }
+
+ // Ignored futures are not uncaught errors.
+ {
+ asyncStart();
+ bool threw = false;
+ runZonedGuarded(() {
+ var c = Completer<int>.sync();
+ var f = c.future;
+ f.ignore();
+ c.completeError("ERROR2");
+ }, (e, s) {
+ threw = true;
+ Expect.fail("Should not happen: $e");
+ });
+ Future.delayed(Duration.zero, () {
+ if (threw) Expect.fail("Future not ignored.");
+ asyncEnd();
+ });
+ }
+ asyncEnd();
+}
diff --git a/tests/lib/async/unawaited_error_test.dart b/tests/lib/async/unawaited_error_test.dart
new file mode 100644
index 0000000..597a9f8
--- /dev/null
+++ b/tests/lib/async/unawaited_error_test.dart
@@ -0,0 +1,11 @@
+// Copyright (c) 2021, 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.
+
+void main() {
+ // The `unawaited` function is not exposed by dart:core.
+ unawaited;
+ // [error line 7, column 3, length 9]
+ // [cfe] Getter not found: 'unawaited'.
+ // [analyzer] COMPILE_TIME_ERROR.UNDEFINED_IDENTIFIER
+}
diff --git a/tests/lib/async/unawaited_test.dart b/tests/lib/async/unawaited_test.dart
new file mode 100644
index 0000000..dc095cc
--- /dev/null
+++ b/tests/lib/async/unawaited_test.dart
@@ -0,0 +1,52 @@
+// Copyright (c) 2021, 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:async_helper/async_helper.dart';
+import "package:expect/expect.dart";
+import 'dart:async' show Completer, runZonedGuarded, unawaited;
+import 'dart:async' as prefix;
+import '../../language/static_type_helper.dart';
+
+void main() {
+ testUnawaited();
+}
+
+void testUnawaited() {
+ // Exists where expected.
+ prefix.unawaited.expectStaticType<Exactly<void Function(Future<Object?>)>>();
+
+ var future = Future<int>.value(42);
+ captureStaticType(unawaited(future), <T>(value) {
+ Expect.equals(typeOf<void>(), T);
+ });
+
+ asyncStart();
+ // Unawaited futures still throw.
+ {
+ var c = Completer<int>();
+ var f = c.future;
+ unawaited(f);
+ asyncStart();
+ f.catchError((e) {
+ Expect.equals("ERROR1", e);
+ asyncEnd();
+ return 0;
+ });
+ c.completeError("ERROR1");
+ }
+ // Unawaited futures are still uncaught errors.
+ {
+ asyncStart();
+ runZonedGuarded(() {
+ var c = Completer<int>();
+ var f = c.future;
+ unawaited(f);
+ c.completeError("ERROR2");
+ }, (e, s) {
+ Expect.equals("ERROR2", e);
+ asyncEnd();
+ });
+ }
+ asyncEnd();
+}
diff --git a/tests/lib_2/async/future_extension_test.dart b/tests/lib_2/async/future_extension_test.dart
new file mode 100644
index 0000000..cd28986
--- /dev/null
+++ b/tests/lib_2/async/future_extension_test.dart
@@ -0,0 +1,54 @@
+// Copyright (c) 2021, 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:async_helper/async_helper.dart';
+import "package:expect/expect.dart";
+import 'dart:async' show Completer, runZonedGuarded;
+import '../../language/static_type_helper.dart';
+
+void main() {
+ testIgnore();
+}
+
+void testIgnore() {
+ var future = Future<int>.value(42);
+ captureStaticType(future.ignore(), <T>(T value) {
+ Expect.equals(typeOf<void>(), T);
+ });
+
+ asyncStart();
+ // Ignored futures can still be listend to.
+ {
+ var c = Completer<int>.sync();
+ var f = c.future;
+ f.ignore();
+ asyncStart();
+ f.catchError((e) {
+ Expect.equals("ERROR1", e);
+ asyncEnd();
+ return 0;
+ });
+ c.completeError("ERROR1");
+ }
+
+ // Ignored futures are not uncaught errors.
+ {
+ asyncStart();
+ bool threw = false;
+ runZonedGuarded(() {
+ var c = Completer<int>.sync();
+ var f = c.future;
+ f.ignore();
+ c.completeError("ERROR2");
+ }, (e, s) {
+ threw = true;
+ Expect.fail("Should not happen: $e");
+ });
+ Future.delayed(Duration.zero, () {
+ if (threw) Expect.fail("Future not ignored.");
+ asyncEnd();
+ });
+ }
+ asyncEnd();
+}
diff --git a/tests/lib_2/async/unawaited_error_test.dart b/tests/lib_2/async/unawaited_error_test.dart
new file mode 100644
index 0000000..597a9f8
--- /dev/null
+++ b/tests/lib_2/async/unawaited_error_test.dart
@@ -0,0 +1,11 @@
+// Copyright (c) 2021, 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.
+
+void main() {
+ // The `unawaited` function is not exposed by dart:core.
+ unawaited;
+ // [error line 7, column 3, length 9]
+ // [cfe] Getter not found: 'unawaited'.
+ // [analyzer] COMPILE_TIME_ERROR.UNDEFINED_IDENTIFIER
+}
diff --git a/tests/lib_2/async/unawaited_test.dart b/tests/lib_2/async/unawaited_test.dart
new file mode 100644
index 0000000..16bd285
--- /dev/null
+++ b/tests/lib_2/async/unawaited_test.dart
@@ -0,0 +1,54 @@
+// Copyright (c) 2021, 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.9
+
+import 'package:async_helper/async_helper.dart';
+import "package:expect/expect.dart";
+import 'dart:async' show Completer, runZonedGuarded, unawaited;
+import 'dart:async' as prefix;
+import '../../language/static_type_helper.dart';
+
+void main() {
+ testUnawaited();
+}
+
+void testUnawaited() {
+ // Exists where expected.
+ prefix.unawaited.expectStaticType<Exactly<void Function(Future<Object>)>>();
+
+ var future = Future<int>.value(42);
+ captureStaticType(unawaited(future), <T>(value) {
+ Expect.equals(typeOf<void>(), T);
+ });
+
+ asyncStart();
+ // Unawaited futures still throw.
+ {
+ var c = Completer<int>();
+ var f = c.future;
+ unawaited(f);
+ asyncStart();
+ f.catchError((e) {
+ Expect.equals("ERROR1", e);
+ asyncEnd();
+ return 0;
+ });
+ c.completeError("ERROR1");
+ }
+ // Unawaited futures are still uncaught errors.
+ {
+ asyncStart();
+ runZonedGuarded(() {
+ var c = Completer<int>();
+ var f = c.future;
+ unawaited(f);
+ c.completeError("ERROR2");
+ }, (e, s) {
+ Expect.equals("ERROR2", e);
+ asyncEnd();
+ });
+ }
+ asyncEnd();
+}
To view, visit change 200428. To unsubscribe, or for help writing mail filters, visit settings.
go/dart-cbuild result: FAILURE (NO REGRESSIONS DETECTED)
Details: https://goto.google.com/dart-cbuild/find/902f149e2c25926310b463a92891169f75024706