-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Description
avoid_futureor_void
Description
Flag the type FutureOr<void> as questionable.
Details
The type FutureOr<void> is inherently a contradictory type.
It is essentially the union of Future<void> and void. However, void is a top type so it is never possible to discriminate the union: Any object whatsoever with static type FutureOr<void> can be intended as the void case, in which case the object should be ignored and discarded.
The other interpretation (which might always be wrong) may occur if we actually have a Future<void> (that is, a Future<T> for any type T). In this case the object should not be ignored. We might want to await it (or pass it on to some other location where it will be handled), or we might want to .ignore() it in order to handle the situation where it is completed with an error, but we should do something (cf., for example, https://dart.dev/tools/linter-rules/unawaited_futures and https://dart.dev/tools/linter-rules/discarded_futures).
This means that any expression whose type is FutureOr<void> is a dilemma, and, as far as possible, we shouldn't have expressions with that type.
The remedy depends on the situation: For return types, it might be possible to replace the type FutureOr<void> by Future<void>?. Future<void>? can be discriminated precisely: If we received null then we know that there is nothing to handle. Otherwise we would have received a Future<void>; the future should be awaited, but result that it is completed with should be discarded (or we could .ignore() the future, but that's in itself a way to handle it). The dilemma is gone!
For parameter types, FutureOr<void> can be replaced by void: This means that the function body will not use that argument (unless it goes to great lengths to cheat us), and the type of the function will remain unchanged (according to the subtype relationships), so it won't break any clients or overrides.
If the function might actually use the future (if any), it should be considered whether the parameter type can be Future<void>?. This is again a clear type because we can discriminate it precisely in the function body. This may be more difficult to do, because the change from parameter type FutureOr<void> to parameter type Future<void>? makes the function a supertype of what it was previously, which is a breaking change in many ways. With the old type we could pass anything whatsoever, with the new type we can only pass null or a future. However, this might arguably be a better type for that function, because it is a less ambiguous characterization of the intended use of that parameter.
Kind
Guard against logical errors where a future is ignored, but should be handled, or vice versa.
Bad Examples
FutureOr<void> f() {...}
SomeType g(FutureOr<void> arg) {...} // `arg` not used.
SomeType h(FutureOr<void> arg) {...} // `arg` used in some cases.Good Examples
Future<void>? f() {...}
SomeType g(void _) {...} // Non-breaking change for callers of `g` above.
SomeType h(Future<void>? maybeFuture) {...} // Breaking change from `h` above.Discussion
There may be situations where a legitimate signature using type variables yields the type FutureOr<void> somewhere, because a type argument turns out to have the value void. For instance, a visitor may yield a result, but some visitors exist only because of their side effects, so they could have Visitor<void> as a supertype, and they might have a resulting member signature where FutureOr<void> occurs.
This is probably not easy to avoid, but it might still be helpful to mark those occurrences as special (by ignoring the lint), and comment on the right interpretation for that particular part of the interface.
Discussion checklist
- List any existing rules this proposal modifies, complements, overlaps or conflicts with. None that I'm aware of.
- List any relevant issues (reported here, the [SDK Tracker], or elsewhere). Allow
return;in function returningFutureOr<void>. language#3246, Future<void>/FutureOr<void> require returns? #32443, Analyzer reports a return-without-value in aFutureOr<void>function; does not report empty function #44480, Change Builder.build to FutureOr<void> build#535, Bogus void_checks warning when a FutureOr<void> callback contains a throw #58621, FutureOr<void> function warns of missing return #35237, void_checks false positive with FutureOr<void> and NNBD #58205, Analyzer: Future<void> requiring return statement? #31278, value-free return should be allowed in function which returns FutureOr<void> language#1370, Meta issue for returns in functions with void related return types #33218. - If there's any prior art (e.g., in other linters), please add references here. None that I'm aware of.
- If this proposal corresponds to [Effective Dart] or [Flutter Style Guide] advice, please call it out. (If there isn't any corresponding advice, should there be?) The guides do not address this issue as far as I can see.
- Real-world examples: Please check the issues mentioned above.