Skip to content

Commit 097261f

Browse files
committed
Auto merge of rust-lang#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: rust-lang#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.
2 parents 10a98e8 + c5ed7b0 commit 097261f

File tree

3 files changed

+83
-0
lines changed

3 files changed

+83
-0
lines changed

compiler/rustc_lint/src/unused.rs

+18
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
251251
/// The root of the normal must_use lint with an optional message.
252252
Def(Span, DefId, Option<Symbol>),
253253
Boxed(Box<Self>),
254+
Pinned(Box<Self>),
254255
Opaque(Box<Self>),
255256
TraitObject(Box<Self>),
256257
TupleElement(Vec<(usize, Self)>),
@@ -284,6 +285,11 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
284285
is_ty_must_use(cx, boxed_ty, expr, span)
285286
.map(|inner| MustUsePath::Boxed(Box::new(inner)))
286287
}
288+
ty::Adt(def, args) if cx.tcx.lang_items().pin_type() == Some(def.did()) => {
289+
let pinned_ty = args.type_at(0);
290+
is_ty_must_use(cx, pinned_ty, expr, span)
291+
.map(|inner| MustUsePath::Pinned(Box::new(inner)))
292+
}
287293
ty::Adt(def, _) => is_def_must_use(cx, def.did(), span),
288294
ty::Alias(ty::Opaque, ty::AliasTy { def_id: def, .. }) => {
289295
elaborate(
@@ -425,6 +431,18 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
425431
expr_is_from_block,
426432
);
427433
}
434+
MustUsePath::Pinned(path) => {
435+
let descr_pre = &format!("{descr_pre}pinned ");
436+
emit_must_use_untranslated(
437+
cx,
438+
path,
439+
descr_pre,
440+
descr_post,
441+
plural_len,
442+
true,
443+
expr_is_from_block,
444+
);
445+
}
428446
MustUsePath::Opaque(path) => {
429447
let descr_pre = &format!("{descr_pre}implementer{plural_suffix} of ");
430448
emit_must_use_untranslated(

tests/ui/lint/unused/must_use-pin.rs

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#![deny(unused_must_use)]
2+
3+
use std::{ops::Deref, pin::Pin};
4+
5+
#[must_use]
6+
struct MustUse;
7+
8+
#[must_use]
9+
struct MustUsePtr<'a, T>(&'a T);
10+
11+
impl<'a, T> Deref for MustUsePtr<'a, T> {
12+
type Target = T;
13+
14+
fn deref(&self) -> &Self::Target {
15+
self.0
16+
}
17+
}
18+
19+
fn pin_ref() -> Pin<&'static ()> {
20+
Pin::new(&())
21+
}
22+
23+
fn pin_ref_mut() -> Pin<&'static mut ()> {
24+
Pin::new(unimplemented!())
25+
}
26+
27+
fn pin_must_use_ptr() -> Pin<MustUsePtr<'static, ()>> {
28+
Pin::new(MustUsePtr(&()))
29+
}
30+
31+
fn pin_box() -> Pin<Box<()>> {
32+
Box::pin(())
33+
}
34+
35+
fn pin_box_must_use() -> Pin<Box<MustUse>> {
36+
Box::pin(MustUse)
37+
}
38+
39+
fn main() {
40+
pin_ref();
41+
pin_ref_mut();
42+
pin_must_use_ptr(); //~ ERROR unused pinned `MustUsePtr` that must be used
43+
pin_box();
44+
pin_box_must_use(); //~ ERROR unused pinned boxed `MustUse` that must be used
45+
}
+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error: unused pinned `MustUsePtr` that must be used
2+
--> $DIR/must_use-pin.rs:42:5
3+
|
4+
LL | pin_must_use_ptr();
5+
| ^^^^^^^^^^^^^^^^^^
6+
|
7+
note: the lint level is defined here
8+
--> $DIR/must_use-pin.rs:1:9
9+
|
10+
LL | #![deny(unused_must_use)]
11+
| ^^^^^^^^^^^^^^^
12+
13+
error: unused pinned boxed `MustUse` that must be used
14+
--> $DIR/must_use-pin.rs:44:5
15+
|
16+
LL | pin_box_must_use();
17+
| ^^^^^^^^^^^^^^^^^^
18+
19+
error: aborting due to 2 previous errors
20+

0 commit comments

Comments
 (0)