Add a lint for forgetting futures - #17054
Conversation
3c86ae2 to
1a5451f
Compare
|
r? @llogiq |
|
CC: @datdenkikniet we talked about this at the embedded unconf |
d0848b3 to
b9bd4f3
Compare
| if let Some(future_trait) = cx.tcx.lang_items().future_trait() | ||
| && implements_trait(cx, arg_ty, future_trait, &[]) | ||
| { | ||
| span_lint_and_then(cx, FORGET_FUTURE, expr.span, FORGET_FUTURE_SUMMARY, |diag| { | ||
| diag.span_note(arg.span, format!("argument has type `{arg_ty}`")); | ||
| }); | ||
| } |
There was a problem hiding this comment.
Aren't you also linting Future types even if they don't implement Drop? What would be the problem there?
There was a problem hiding this comment.
Oh yes that is a very good point. I'll change that
There was a problem hiding this comment.
Weirdly the future created by an async block or async fn always implements Drop? No matter if it captures anything that implements Drop.
There was a problem hiding this comment.
Weirdly the future created by an
asyncblock orasync fnalways implementsDrop? No matter if it captures anything that implementsDrop.
I'd expect that, at least if any object inside the async block implements Drop and can be alive around any .await. You should try and check if this is easy to determine.
There was a problem hiding this comment.
It looks like the drop glue is always generated, but no explicit Drop impl is around. So for example this will fail to compile:
async fn foo() {
let x = Box::new(());
core::future::ready(()).await;
let _ = x;
}
fn bar() -> impl Future<Output = ()> + Drop {
foo()
}Are you by any chance also at RustWeek at the moment?
There was a problem hiding this comment.
Are you by any chance also at RustWeek at the moment?
Yes, but leaving right after lunch.
This comment has been minimized.
This comment has been minimized.
49380f2 to
ab5baf5
Compare
This comment has been minimized.
This comment has been minimized.
ab5baf5 to
5e2808c
Compare
|
I finally found some time again to work on this after RustWeek and addressed the suggestions by @datdenkikniet (thanks those texts were much better than what I came up with). The Drop glue that is generated still makes this lint complain about more cases than it strictly needs to. |
5e2808c to
aeced90
Compare
| Dropping such a type only extends its contained lifetimes"; | ||
| const FORGET_NON_DROP_SUMMARY: &str = "call to `std::mem::forget` with a value that does not implement `Drop`. \ | ||
| Forgetting such a type is the same as dropping it"; | ||
| const FORGET_FUTURE_SUMMARY: &str = "forgetting a Future might cause problems with cancelation"; |
There was a problem hiding this comment.
Not sure what clippy's stance is, but from googling: cancelation is used, but cancellation is much more common. We might want to switch to that instead.
This comment has been minimized.
This comment has been minimized.
c7e8e43 to
0802178
Compare
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Co-authored-by: datdenkikniet <jcdra1@gmail.com>
0802178 to
9ae7fc0
Compare
|
This PR was rebased onto a different master commit. Here's a range-diff highlighting what actually changed. Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers. |
|
Hey @llogiq I started this back at the All-Hands, would you still be available to review this? |
| = note: argument has type `std::string::String` | ||
|
|
||
| error: aborting due to 4 previous errors | ||
| error: usage of `mem::forget` on type with `Drop` fields |
There was a problem hiding this comment.
This means that we will lint both forget_future and mem_forget if the latter is active. I think we should not lint mem_forget if we already lint forget_future, as the latter is more specific.
The easiest way to do this is to merge both lint passes and return from check_expr right after linting forget_future and only otherwise lint mem_forget.
There was a problem hiding this comment.
Oh I did not think about this. The only problem I see with this might be that if a project already set mem_forget to deny, then using mem::forget() on a future would go from an error to a warning. I guess I could also check if the lint level of mem_forget is higher than that of forget_future and in that case emit both or just mem_forget?
|
Reminder, once the PR becomes ready for a review, use |
|
@rustbot ready |
View all comments
This adds a new lint to check when a Future is passed to
mem::forget, thus not running any cancellation logic.changelog: Add [
forget_future] lint