Description
Describe the issue
The lint prefer_void_to_null
is probably obsolete. It is certainly possible that it has been helpful in the effort to eliminate some suboptimal usages of the type Null
, but it is most likely not a good fit for the language today. I'd recommend that this lint is removed entirely.
To Reproduce
Several examples have been given where this lint emits a message, but the advice is not applicable or useful. Here is one example:
void main() {
List<(int?, int?)> xs;
...
xs = <(Null, int)>[(null, 10)]; // LINT
}
In this case it is not possible to replace the type Null
by the type void
, as recommended by the lint (it's a compile-time error to do that, as it should be). Also, the chosen types may be slightly odd, but they do make sense in this context.
Further discussions and examples can be found in several issues:
- False positive with
prefer_void_to_null
#59179 prefer_void_to_null
false positive in case of nullable generics #58959- Adjust documentation of prefer_void_to_null #58908
void
should not have the same behavior asdynamic
#53759
Finally, consider the examples given in the documentation for the lint itself (renaming the declarations because they were all named f
):
// BAD:
Null f1() {}
Future<Null> f2() {}
Stream<Null> f3() {}
f4(Null x) {}
// GOOD:
void f1() {}
Future<void> f2() {}
Stream<void> f3() {}
f4(void x) {}
It is not obvious why the type Null
has been used in the signature of these functions (and some of them might actually need to be updated to use Never
today in order to make sense), but we should note that all of those changes would be breaking.
If f1
is called and the result is assigned to a variable of type T?
for any T
then it would now be a compile-time error. Similarly for f2
and f3
if the returned value is assigned to Future<T?>
or Stream<T?>
for any T
. Finally, f4
could be an instance method which is overridden by f4(int? iq)
, and the change to f4(void x) {}
would now break that override.
The motivation for the lint was probably that this is good breakage, because those functions should not have been declared using the type Null
in the first place. However, it is not obvious to me that this kind of motivation is justified today: Is it really true that the change from Null
to void
is useful, and worth the breakage?
I tend to think that the spurious occurrence of null at run time used to be an issue worth fighting against, but with null safety in the type system we already know how to avoid null when it really shouldn't occur, and if nulls are allowed in any given context then every non-Object?
usage of the null will be guarded by a null check.
Expected behavior
The lint should only give advice which is applicable and useful, but it seems to produce a large amount of false positives.
Additional context
Null safety has completely changed the role played by the type Null
. In this new context, prefer_void_to_null
does not play a constructive role in most of the situations where it used to be helpful. I don't think there is a natural and useful modification which can be used to bring this lint up to date, hence I'm suggesting that get gets removed.
Does anyone have some really compelling reasons why this lint is still useful?