-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Description
Issue description
As the title says the analysis of the discarded_futures and unawaited_futures does not work for tearoffs used as arguments. Futhermore, in closures, the reporting of issues is dependent on whether the enclosing function is async or not (instead of evaluating that for the closure itself). Finally, unawaited completely disables issue reporting for all futures used inside it, even if they are discarded in closures, which can lead to errors.
The following examples were analyzed using dart analyze, with the rules discarded_futures and unawaited_futures enabled.
I have created a gist here, which contains the execution logs of these examples, along with some print statements, to show that this incomplete analysis can lead to errors in real-world applications.
Example 1 (analysis does not work for tearoffs used as arguments + in closures, is dependent on whether enclosing function is async)
In this example, loadFromRemote is a function that returns a Future<void>. execute accepts a callback which is a function that returns a void. Therefore, by calling callback, the future returned by loadFromRemote is discarded, without the use of unawaited or ignore [unexpected].
import 'dart:async';
Future<void> loadFromRemote() async {
await Future.delayed(const Duration(seconds: 1));
}
void execute(void Function() callback) {
callback();
}
void load() {
execute(loadFromRemote);
}Notes:
- If
loadFromRemoteis called inside a closure, with() => loadFromRemote(), thediscarded_futureslint rule creates an issue [expected]. If, after this change,loadis modified to beasync, the issue disappears [unexpected].
Example 2 (in closures, is dependent on whether enclosing function is async)
In this example, there are two functions, tryLoadFromRemote and tryLoadFromCache that both return Future<void>. Then, there is a function, load, that also returns Future<void> and is async. tryLoadFromCache is used in load and is awaited, but tryLoadFromRemote, which is used inside a sync closure in then, is not awaited and, because of that, is discarded. No issue is reported on the tryLoadFromCache call [expected], but the tryLoadFromRemote call does not report any issues [unexpected].
import 'dart:async';
Future<void> tryLoadFromRemote() async {
await Future.delayed(const Duration(seconds: 1));
}
Future<void> tryLoadFromCache() async {}
Future<void> load() async {
await tryLoadFromCache().then((_) {
tryLoadFromRemote();
});
}Notes:
-
If the signature of
loadis changed tovoid load()and theawaitkeyword is removed, both calls report issues [expected]. If the code is wrapped inunawaited(see below), no issues are reported on thetryLoadFromCachecall [expected] and on thetryLoadFromRemotecall [unexpected].void load() { unawaited(tryLoadFromCache().then((_) { tryLoadFromRemote(); })); }
General info
- Dart 3.5.3 (stable) (Wed Sep 11 16:22:47 2024 +0000) on "linux_x64"
- on linux / Linux 6.11.3-200.fc40.x86_64
#1SMP PREEMPT_DYNAMIC Thu Oct 10 22:31:19 UTC 2024 - locale is en_US.UTF-8
Process info
| Memory | CPU | Elapsed time | Command line |
|---|---|---|---|
| 331 MB | 133.0% | 00:02 | dart language-server --protocol=lsp --client-id=VS-Code --client-version=3.98.1 |
| 79 MB | 22.0% | 00:02 | dart tooling-daemon --machine |