Skip to content

Commit

Permalink
Auto merge of #118054 - max-niederman:pinned-must-use, r=Nilstrieb
Browse files Browse the repository at this point in the history
Lint pinned `#[must_use]` pointers (in particular, `Box<T>` where `T` is `#[must_use]`) in `unused_must_use`.

Fixes: #111458

This is motivated by a common async/await pattern:

```rs
fn foo() -> Pin<Box<dyn Future<Output = i32>>> {
    Box::pin(async { 42 })
}

// call `foo`, but forget to await the result
foo();
```

Unlike with `async fn` or return position `impl Future`, this does not currently warn the user that the `Future` is unused.

To fix this, I've extended the `unused_must_use` lint to catch `Pin<P>`, where `P` must be used. In particular, this applies to `Pin<Box<T>>`, where `T` must be used. I'm not sure if there are other pointers where this applies, but I can't think of any situation the user wouldn't want to be warned.
  • Loading branch information
bors committed Nov 19, 2023
2 parents 10a98e8 + c5ed7b0 commit 097261f
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 0 deletions.
18 changes: 18 additions & 0 deletions compiler/rustc_lint/src/unused.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
/// The root of the normal must_use lint with an optional message.
Def(Span, DefId, Option<Symbol>),
Boxed(Box<Self>),
Pinned(Box<Self>),
Opaque(Box<Self>),
TraitObject(Box<Self>),
TupleElement(Vec<(usize, Self)>),
Expand Down Expand Up @@ -284,6 +285,11 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
is_ty_must_use(cx, boxed_ty, expr, span)
.map(|inner| MustUsePath::Boxed(Box::new(inner)))
}
ty::Adt(def, args) if cx.tcx.lang_items().pin_type() == Some(def.did()) => {
let pinned_ty = args.type_at(0);
is_ty_must_use(cx, pinned_ty, expr, span)
.map(|inner| MustUsePath::Pinned(Box::new(inner)))
}
ty::Adt(def, _) => is_def_must_use(cx, def.did(), span),
ty::Alias(ty::Opaque, ty::AliasTy { def_id: def, .. }) => {
elaborate(
Expand Down Expand Up @@ -425,6 +431,18 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
expr_is_from_block,
);
}
MustUsePath::Pinned(path) => {
let descr_pre = &format!("{descr_pre}pinned ");
emit_must_use_untranslated(
cx,
path,
descr_pre,
descr_post,
plural_len,
true,
expr_is_from_block,
);
}
MustUsePath::Opaque(path) => {
let descr_pre = &format!("{descr_pre}implementer{plural_suffix} of ");
emit_must_use_untranslated(
Expand Down
45 changes: 45 additions & 0 deletions tests/ui/lint/unused/must_use-pin.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#![deny(unused_must_use)]

use std::{ops::Deref, pin::Pin};

#[must_use]
struct MustUse;

#[must_use]
struct MustUsePtr<'a, T>(&'a T);

impl<'a, T> Deref for MustUsePtr<'a, T> {
type Target = T;

fn deref(&self) -> &Self::Target {
self.0
}
}

fn pin_ref() -> Pin<&'static ()> {
Pin::new(&())
}

fn pin_ref_mut() -> Pin<&'static mut ()> {
Pin::new(unimplemented!())
}

fn pin_must_use_ptr() -> Pin<MustUsePtr<'static, ()>> {
Pin::new(MustUsePtr(&()))
}

fn pin_box() -> Pin<Box<()>> {
Box::pin(())
}

fn pin_box_must_use() -> Pin<Box<MustUse>> {
Box::pin(MustUse)
}

fn main() {
pin_ref();
pin_ref_mut();
pin_must_use_ptr(); //~ ERROR unused pinned `MustUsePtr` that must be used
pin_box();
pin_box_must_use(); //~ ERROR unused pinned boxed `MustUse` that must be used
}
20 changes: 20 additions & 0 deletions tests/ui/lint/unused/must_use-pin.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
error: unused pinned `MustUsePtr` that must be used
--> $DIR/must_use-pin.rs:42:5
|
LL | pin_must_use_ptr();
| ^^^^^^^^^^^^^^^^^^
|
note: the lint level is defined here
--> $DIR/must_use-pin.rs:1:9
|
LL | #![deny(unused_must_use)]
| ^^^^^^^^^^^^^^^

error: unused pinned boxed `MustUse` that must be used
--> $DIR/must_use-pin.rs:44:5
|
LL | pin_box_must_use();
| ^^^^^^^^^^^^^^^^^^

error: aborting due to 2 previous errors

0 comments on commit 097261f

Please sign in to comment.