-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Generalize handling of error-prone situations where an object is discarded #58650
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
Comments
Yes, we could definitely generalize the lint support for these cases, and I like the idea of doing so. But I think this raises a very interesting design question about how best to support such checks. I haven't formed any strong opinions yet, but I'll share a couple of thoughts. We've had discussions several times about the appropriate scope for lint rules. We haven't formulated any concrete principles other than the obvious one of trying to do what's best for users. For example, we currently have three similar lints around returning A similar question arises here. Should we have a single lint that catches all of the places where a And then, of course, we have another mechanism that might be more appropriate if we think we should consolidate all of this checking into one, or a small number, of lints. We have support in the analysis options file to specify when we should effectively be using a stricter definition of the language. Users can currently enable |
We might be able to have lints that amount to sets of "smaller" lints. A developer could then enable each of the small lints separately for better control, or they could enable the "big" lint that simply enables every lint that is a member of that set. |
My broad take on this is that: 1) this seems like a useful extension to the lint (or possibly a different lint, but in general keeping things simpler seems better) and 2) that this is inherently going to be very heuristic and specific to Futures (or else extraordinarily complicated), so I'm skeptical about generalizing this too much. On 2, note that if you want to generalize this fully, things like generics are going to make your life complicated. You can't in general know statically whether a type variable
This seems most reasonable to me, but other folks involved in the original discussion probably have better context, perhaps they should be looped in here?
My initial response is, "no". Futures are very special here. While I can imagine a coding style that forbids implicitly discarding any value, that feels draconian to me.
My initial take is no, though perhaps I could be convinced otherwise? Certainly not all objects in general, that feels, as I say above, really draconian. Even the |
The "we" is primarily Phil and I, so I can't think of anyone else to loop in at the moment.
I completely agree that the strict checks should have no false positives, and that's a compelling argument for leaving it as a lint.
Unless we hear from users that they need to have more granularity, I agree, having fewer lints is generally better. The one potentially mitigating factor is that changing a lint to start producing diagnostics in more cases can be a breaking change (if code that previously built cleanly starts failing). We'll need to measure the impact of such a change in order to make a decision. |
There was an internal discussion thread among a number of people that I believe inspired this issue. |
I was one of the folks raising this issue as a source of errors. From this perspective, a single lint that catches all places where a |
The lint
unawaited_futures
is concerned with situations where a future is computed and then discarded (for example, a statement isfoo();
rather thanawait foo();
, andfoo
returns a future). This situation is considered error-prone because it is likely to cause some computations to occur "too late". The situation is hard to detect without tool support, because it is an implicit and perfectly normal thing to discard an object (of any type).However, #57769 makes the point that this lint is currently ignoring many situations where a future is discarded: If the future is assigned to a variable or passed to a parameter whose declared type is
void
orObject
or a top type, any static knowledge that there exists a future that may need to be awaited is lost, and in the case where the static type of the future will bevoid
we even have support to ensure that the object is discarded.Another incompleteness in the checking performed by
unawaited_futures
is the fact that higher order occurrences are ignored:If, for any
T
, we assign a function of typeFuture<T> Function()
to a variable/parameter of typevoid Function()
orObject Function()
then it is likely that every invocation of said function will discard a future, which is just as error-prone as the first-order case.Another higher-order case arises when
Future<...>
is used as a type argument; for instance, assigning aList<Future<T>>
to a variable/parameter of typeList<void>
orList<Object>
will destroy the information that each element in that list might need to be awaited.Issue #57653 notes that all the above lints will ignore functions whose body isn't marked
async
. This means that developers will not get the heads up in the situation where a functionfoo
is not markedasync
, but it callsbar
, andbar
has now been updated to return a future.We have a similar situation with return statements, for arbitrary types (not just futures, but anything which doesn't have type
void
ordynamic
):void foo() { return 1; }
is an error (not just a lint or hint!), because it is considered error-prone to make it look like a value of typeint
is being delivered to a caller, but it is effectively discarded because of the return typevoid
. Similar rules kick in when a non-void expression is returned from anasync
function whose return type isvoid
orFuture<void>
.Finally, we have an object named
useResult
in package 'meta', and this object is used as metadata on functions whose returned value should not be discarded. In this case we would also have higher-order variants of the situations which are currently linted, and those higher-order variants are not detected.Could we generalize the lint support for flagging error-prone situations where an object is discarded, such that it is at least possible to detect higher-order cases, and the mechanism can be used for other types than
Future
and/orvoid
? Is it just a matter of makinguseResult
more powerful, and perhaps adding support for auseType
annotation such that we can do@useType
on the classFuture
, or implicitly add@useResult
on allasync
functions whose return type isn'tvoid
?Cf. #57653, #57769, #58622.
The text was updated successfully, but these errors were encountered: