-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Eliminate prefer_void_to_null
?
#59309
Comments
I agree I think it has outlived its usefulness; I was getting analysis_server compliant and it was pretty awkward; there were plenty of valid uses of |
I'm inclined to agree. Thanks for opening this @eernstg! |
I'm not sure the lint is that bad, as long as it's restrained to where it fits best. Where the lint makes sense is function parameter and return types. The lint shouldn't apply anywhere else. That might be the issue here, it's being over-applied to places where you do care. So the real problem is false positives. If a return type is constrained so that it cannot be made It may be possible to have other false positives, where a return type really should be |
The main reason why I think this lint is less helpful than it used to be is that the main motivation has been transformed drastically. In pre-null-safety code we could have this, with no compile-time errors: // @dart = 2.9
Future<Null> f() async {
return;
}
void main() async {
int i = await f(); // Look out!
} At the comment, we'd initialize Future<Null> f() async {
return;
}
void main() async {
int i = await f(); // Compile-time error, not assignable.
Null n = await f(); // OK. Silly, but honest.
int? j = await f(); // OK, but doesn't forget about null.
j.isEven; // Compile-time error, we still haven't forgotten about null.
} Hence, because we're tracking null precisely today, the null object doesn't get a chance to escape scrutiny the way it did in the olden days. I think this means that the lint doesn't help anyone so much today. @lrhn wrote:
This is a point well taken. An expression with static type However, the null object is actually a quite powerful representation of "nothing to see here!", because it has a safe dynamic test: If an expression has static type An expression of type In other words, we should probably get used to the idea that |
This would be great. Can it be known? This is the example from the analysis_server package: String? foo() {
if (1 == 2) return reportBad();
// ...
}
Null reportBad() {
print('bad');
return null;
} Changing |
I'd say no: The analyzer needs to be able to perform the static analysis on a library knowing only the transitive closure of its imports, and we have no reason to assume that there aren't thousands of clients (in libraries that aren't in that transitive closure) who are depending on the given function/method declaration. Each of them may break if the return type is modified to a (much) more general type.
I think this is a good example, showing that we should just stop shouting at |
It's hard to know why someone chose So while there are cases where it's correct to recommend
|
I think this supports the view that |
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 typeNull
, 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:
In this case it is not possible to replace the type
Null
by the typevoid
, 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:
prefer_void_to_null
#59179prefer_void_to_null
false positive in case of nullable generics #58959void
should not have the same behavior asdynamic
#53759Finally, consider the examples given in the documentation for the lint itself (renaming the declarations because they were all named
f
):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 useNever
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 typeT?
for anyT
then it would now be a compile-time error. Similarly forf2
andf3
if the returned value is assigned toFuture<T?>
orStream<T?>
for anyT
. Finally,f4
could be an instance method which is overridden byf4(int? iq)
, and the change tof4(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 fromNull
tovoid
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?
The text was updated successfully, but these errors were encountered: