about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-11-19 12:23:59 +0000
committerbors <bors@rust-lang.org>2023-11-19 12:23:59 +0000
commit097261f241d0295a84a1fc754639e58202ea7e8e (patch)
treeada2eaff0c5485ef44c05df190933c94a37625b6
parent10a98e8bff0b1335e7f0f1ea0453ee0765ae96fe (diff)
parentc5ed7b0ead27a9b666c2c4fc14160fba972df793 (diff)
Auto merge of #118054 - max-niederman:pinned-must-use, r=Nilstrieb
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.
-rw-r--r--compiler/rustc_lint/src/unused.rs18
-rw-r--r--tests/ui/lint/unused/must_use-pin.rs45
-rw-r--r--tests/ui/lint/unused/must_use-pin.stderr20
3 files changed, 83 insertions, 0 deletions
diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs
index 355855b8e2b..0ae91ac28a7 100644
--- a/compiler/rustc_lint/src/unused.rs
+++ b/compiler/rustc_lint/src/unused.rs
@@ -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)>),
@@ -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(
@@ -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(
diff --git a/tests/ui/lint/unused/must_use-pin.rs b/tests/ui/lint/unused/must_use-pin.rs
new file mode 100644
index 00000000000..b08515428b1
--- /dev/null
+++ b/tests/ui/lint/unused/must_use-pin.rs
@@ -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
+}
diff --git a/tests/ui/lint/unused/must_use-pin.stderr b/tests/ui/lint/unused/must_use-pin.stderr
new file mode 100644
index 00000000000..c04f8fef487
--- /dev/null
+++ b/tests/ui/lint/unused/must_use-pin.stderr
@@ -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
+