Skip to content

Commit 243496e

Browse files
committed
Consider #[must_use] annotation on async fn as also affecting the Future::Output
No longer lint against `#[must_use] async fn foo()`. When encountering a statement that awaits on a `Future`, check if the `Future`'s parent item is annotated with `#[must_use]` and emit a lint if so. This effectively makes `must_use` an annotation on the `Future::Output` instead of only the `Future` itself. Fix #78149.
1 parent 50bb7a4 commit 243496e

File tree

4 files changed

+93
-43
lines changed

4 files changed

+93
-43
lines changed

compiler/rustc_lint/src/unused.rs

+13-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use rustc_hir::def::{DefKind, Res};
99
use rustc_hir::def_id::DefId;
1010
use rustc_infer::traits::util::elaborate_predicates_with_span;
1111
use rustc_middle::ty::adjustment;
12-
use rustc_middle::ty::{self, Ty};
12+
use rustc_middle::ty::{self, DefIdTree, Ty};
1313
use rustc_span::symbol::Symbol;
1414
use rustc_span::symbol::{kw, sym};
1515
use rustc_span::{BytePos, Span};
@@ -93,6 +93,18 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
9393
return;
9494
}
9595

96+
if let hir::ExprKind::Match(await_expr, _arms, hir::MatchSource::AwaitDesugar) = expr.kind
97+
&& let ty = cx.typeck_results().expr_ty(&await_expr)
98+
&& let ty::Opaque(def_id, _) = ty.kind()
99+
&& cx.tcx.ty_is_opaque_future(ty)
100+
&& let parent = cx.tcx.parent(*def_id)
101+
&& check_must_use_def(cx, parent, expr.span, "awaited future returned by ", "")
102+
{
103+
// We have a bare `foo().await;` on an opaque type from an async function that was
104+
// annotated with `#[must_use]`.
105+
return;
106+
}
107+
96108
let ty = cx.typeck_results().expr_ty(&expr);
97109
let type_permits_lack_of_use = check_must_use_ty(cx, ty, &expr, expr.span, "", "", 1);
98110

compiler/rustc_passes/src/check_attr.rs

+2-12
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ impl CheckAttrVisitor<'_> {
139139
sym::collapse_debuginfo => self.check_collapse_debuginfo(attr, span, target),
140140
sym::const_trait => self.check_const_trait(attr, span, target),
141141
sym::must_not_suspend => self.check_must_not_suspend(&attr, span, target),
142-
sym::must_use => self.check_must_use(hir_id, &attr, span, target),
142+
sym::must_use => self.check_must_use(hir_id, &attr, target),
143143
sym::rustc_pass_by_value => self.check_pass_by_value(&attr, span, target),
144144
sym::rustc_allow_incoherent_impl => {
145145
self.check_allow_incoherent_impl(&attr, span, target)
@@ -1163,17 +1163,7 @@ impl CheckAttrVisitor<'_> {
11631163
}
11641164

11651165
/// Warns against some misuses of `#[must_use]`
1166-
fn check_must_use(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool {
1167-
let node = self.tcx.hir().get(hir_id);
1168-
if let Some(kind) = node.fn_kind() && let rustc_hir::IsAsync::Async = kind.asyncness() {
1169-
self.tcx.emit_spanned_lint(
1170-
UNUSED_ATTRIBUTES,
1171-
hir_id,
1172-
attr.span,
1173-
errors::MustUseAsync { span }
1174-
);
1175-
}
1176-
1166+
fn check_must_use(&self, hir_id: HirId, attr: &Attribute, target: Target) -> bool {
11771167
if !matches!(
11781168
target,
11791169
Target::Fn

src/test/ui/lint/unused/unused-async.rs

+24-5
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,43 @@
11
// edition:2018
2-
// run-pass
3-
#![allow(dead_code)]
2+
#![deny(unused_must_use)]
3+
44

55
#[must_use]
6-
//~^ WARNING `must_use`
7-
async fn test() -> i32 {
6+
async fn foo() -> i32 {
87
1
98
}
109

10+
#[must_use]
11+
fn bar() -> impl std::future::Future<Output=i32> {
12+
async {
13+
42
14+
}
15+
}
16+
17+
async fn baz() -> i32 {
18+
0
19+
}
1120

1221
struct Wowee {}
1322

1423
impl Wowee {
1524
#[must_use]
16-
//~^ WARNING `must_use`
1725
async fn test_method() -> i32 {
1826
1
1927
}
2028
}
2129

30+
async fn test() {
31+
foo(); //~ ERROR unused return value of `foo` that must be used
32+
//~^ ERROR unused implementer of `Future` that must be used
33+
foo().await; //~ ERROR unused awaited future returned by `foo` that must be used
34+
bar(); //~ ERROR unused return value of `bar` that must be used
35+
//~^ ERROR unused implementer of `Future` that must be used
36+
bar().await; //~ ERROR unused awaited future returned by `bar` that must be used
37+
baz(); //~ ERROR unused implementer of `Future` that must be used
38+
baz().await; // ok
39+
}
40+
2241
/* FIXME(guswynn) update this test when async-fn-in-traits works
2342
trait Doer {
2443
#[must_use]
+54-25
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,55 @@
1-
warning: `must_use` attribute on `async` functions applies to the anonymous `Future` returned by the function, not the value within
2-
--> $DIR/unused-async.rs:5:1
3-
|
4-
LL | #[must_use]
5-
| ^^^^^^^^^^^
6-
LL |
7-
LL | / async fn test() -> i32 {
8-
LL | | 1
9-
LL | | }
10-
| |_- this attribute does nothing, the `Future`s returned by async functions are already `must_use`
11-
|
12-
= note: `#[warn(unused_attributes)]` on by default
13-
14-
warning: `must_use` attribute on `async` functions applies to the anonymous `Future` returned by the function, not the value within
15-
--> $DIR/unused-async.rs:15:5
16-
|
17-
LL | #[must_use]
18-
| ^^^^^^^^^^^
19-
LL |
20-
LL | / async fn test_method() -> i32 {
21-
LL | | 1
22-
LL | | }
23-
| |_____- this attribute does nothing, the `Future`s returned by async functions are already `must_use`
24-
25-
warning: 2 warnings emitted
1+
error: unused implementer of `Future` that must be used
2+
--> $DIR/unused-async.rs:31:5
3+
|
4+
LL | foo();
5+
| ^^^^^
6+
|
7+
note: the lint level is defined here
8+
--> $DIR/unused-async.rs:2:9
9+
|
10+
LL | #![deny(unused_must_use)]
11+
| ^^^^^^^^^^^^^^^
12+
= note: futures do nothing unless you `.await` or poll them
13+
14+
error: unused return value of `foo` that must be used
15+
--> $DIR/unused-async.rs:31:5
16+
|
17+
LL | foo();
18+
| ^^^^^
19+
20+
error: unused awaited future returned by `foo` that must be used
21+
--> $DIR/unused-async.rs:33:5
22+
|
23+
LL | foo().await;
24+
| ^^^^^^^^^^^
25+
26+
error: unused implementer of `Future` that must be used
27+
--> $DIR/unused-async.rs:34:5
28+
|
29+
LL | bar();
30+
| ^^^^^
31+
|
32+
= note: futures do nothing unless you `.await` or poll them
33+
34+
error: unused return value of `bar` that must be used
35+
--> $DIR/unused-async.rs:34:5
36+
|
37+
LL | bar();
38+
| ^^^^^
39+
40+
error: unused awaited future returned by `bar` that must be used
41+
--> $DIR/unused-async.rs:36:5
42+
|
43+
LL | bar().await;
44+
| ^^^^^^^^^^^
45+
46+
error: unused implementer of `Future` that must be used
47+
--> $DIR/unused-async.rs:37:5
48+
|
49+
LL | baz();
50+
| ^^^^^
51+
|
52+
= note: futures do nothing unless you `.await` or poll them
53+
54+
error: aborting due to 7 previous errors
2655

0 commit comments

Comments
 (0)