discarded-futures

Same as discarded_futures in Dart lints

Path
lint/correctness/discarded-futures
Default
Off, opt in
Origin
Dart lints
Rule
220 of 512

Don't discard Futures returned by calls in synchronous functions.

A Future created inside a non-async function and then dropped can't be awaited, so its errors and completion are lost:

void save() {
  writeFile(); // BAD
}

Future<void> save() async {
  await writeFile(); // GOOD
}

Expression statements, cascade sections, and string interpolations whose resolved type is Future (or a subtype) or FutureOr are reported inside synchronous bodies. Assignments, Future.delayed(duration, computation), Map.putIfAbsent, and members annotated @awaitNotRequired, including inherited, extension, and local functions, are exempt; local annotations follow the visible declaration rather than a reusable name. Function typedef annotations follow the invoked value's immediate alias through inferred variables, fields, records, and collections, but not explicit .call or an unannotated alias of that typedef. Member calls, including null-aware accesses, use the reached receiver branch for annotation lookup, as do bounded type parameters and flow-promoted FutureOr values. Unresolved types stay silent. Calls through getters underline the complete callee expression, while method invocations underline only the method name.

Invalid

example.dartdart
import 'dart:async';

Future<int> fetch() async => 1;

FutureOr<int> maybeFetch() => 1;

class Api {
  Future<void> save() async {}

  Future<int> get pending async => 1;
}

void callsFunction() {
  fetch();
}

void callsMethod(Api api) {
  api.save();
}

void readsGetter(Api api) {
  api.pending;
}

void callsFutureOr() {
  maybeFetch();
}

void cascade(Api api) {
  api..save();
}

void interpolates() {
  print('${fetch()}');
}

void inClosure() {
  final callback = () {
    fetch();
  };
  callback();
}

class Widget {
  void initState() {
    fetch();
  }
}
Avoid invoking an asynchronous function in a non-`async` function.
13void callsFunction() {
14 fetch();
Avoid invoking an asynchronous function in a non-`async` function.
17void callsMethod(Api api) {
18 api.save();
Avoid invoking an asynchronous function in a non-`async` function.
21void readsGetter(Api api) {
22 api.pending;
Avoid invoking a function returning `FutureOr` in a non-`async` function.
25void callsFutureOr() {
26 maybeFetch();
Avoid invoking an asynchronous function in a non-`async` function.
29void cascade(Api api) {
30 api..save();
Avoid invoking an asynchronous function in a non-`async` function.
33void interpolates() {
34 print('${fetch()}');
Avoid invoking an asynchronous function in a non-`async` function.
38 final callback = () {
39 fetch();
Avoid invoking an asynchronous function in a non-`async` function.
45 void initState() {
46 fetch();

Valid

example.dartdart
import 'dart:async';
import 'package:meta/meta.dart' show awaitNotRequired;
import 'package:meta/meta.dart' as meta;

Future<int> fetch() async => 1;

class Api {
  Future<void> save() async {}
}

Future<void> asyncBody() async {
  fetch();
  await fetch();
}

void assigned() {
  final future = fetch();
  future.ignore();
}

Future<int> returned() {
  return fetch();
}

void unawaitedCall() {
  unawaited(fetch());
}

void delayed() {
  Future.delayed(Duration.zero, () => 1);
}

void putIfAbsent(Map<String, Future<int>> cache) {
  cache.putIfAbsent('key', fetch);
}

Future<int> arrow() => fetch();

void syncValue() {
  print(1);
}

void assignment(Api api) {
  Future<void> pending;
  pending = api.save();
  pending.ignore();
}

void annotatedLocalFunctions() {
  @awaitNotRequired
  Future<void> handleError() async {}
  handleError();
  print('${handleError()}');

  @meta.awaitNotRequired
  Future<void> prefixed() async {}
  prefixed();
}

Configure discarded-futures

Set its severity in falcon.json. See the configuration guide.

falcon.jsonjson
{
  "linter": {
    "rules": {
      "correctness": {
        "discarded-futures": "error"
      }
    }
  }
}

Or silence one occurrence with a suppression comment; the reason is required.

example.dartdart
// falcon-ignore lint/correctness/discarded-futures: <reason>
  • dispose-fieldsrecommendedflutter

    Requires other owned disposable State fields to be released in dispose().

  • dispose-focus-node-and-timerrecommendedflutter

    Requires owned FocusNode and Timer fields to be released in State.dispose().

  • dispose-provided-instancesrecommendedriverpod

    Flags provider-created instances with a dispose method that lack ref.onDispose cleanup.

  • dispose-provider-resourcesrecommendedriverpod

    Flags provider-created resources without autoDispose or ref.onDispose cleanup.

  • dispose-providersrecommendedprovider

    Requires cleanup callbacks for disposable values created by package:provider's Provider.

  • emit-new-bloc-state-instancesrecommendedbloc

    Flags Bloc and Cubit emissions that reuse their current state instance.

  • exhaustive-casesexhaustive_casesrecommended

    Require switch statements over enum-like classes to cover every constant value.

  • function-always-returns-null

    Flags value functions with a proven explicit null return on every path.