about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEsteban Küber <esteban@kuber.com.ar>2022-08-16 07:56:42 -0700
committerEsteban Küber <esteban@kuber.com.ar>2022-11-10 18:01:03 -0800
commit243496e1299bc6dddb3824a91f026da34f732199 (patch)
treebb0052f9b3ae5bf5a2cbafaaf548b60f163f31cb
parent50bb7a40e11918a93e19ca44ca8893e3b56a8ec8 (diff)
downloadrust-243496e1299bc6dddb3824a91f026da34f732199.tar.gz
rust-243496e1299bc6dddb3824a91f026da34f732199.zip
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.
-rw-r--r--compiler/rustc_lint/src/unused.rs14
-rw-r--r--compiler/rustc_passes/src/check_attr.rs14
-rw-r--r--src/test/ui/lint/unused/unused-async.rs29
-rw-r--r--src/test/ui/lint/unused/unused-async.stderr79
4 files changed, 93 insertions, 43 deletions
diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs
index 3e8bd7a374d..1a5515530ae 100644
--- a/compiler/rustc_lint/src/unused.rs
+++ b/compiler/rustc_lint/src/unused.rs
@@ -9,7 +9,7 @@ use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
 use rustc_infer::traits::util::elaborate_predicates_with_span;
 use rustc_middle::ty::adjustment;
-use rustc_middle::ty::{self, Ty};
+use rustc_middle::ty::{self, DefIdTree, Ty};
 use rustc_span::symbol::Symbol;
 use rustc_span::symbol::{kw, sym};
 use rustc_span::{BytePos, Span};
@@ -93,6 +93,18 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
             return;
         }
 
+        if let hir::ExprKind::Match(await_expr, _arms, hir::MatchSource::AwaitDesugar) = expr.kind
+            && let ty = cx.typeck_results().expr_ty(&await_expr)
+            && let ty::Opaque(def_id, _) = ty.kind()
+            && cx.tcx.ty_is_opaque_future(ty)
+            && let parent = cx.tcx.parent(*def_id)
+            && check_must_use_def(cx, parent, expr.span, "awaited future returned by ", "")
+        {
+            // We have a bare `foo().await;` on an opaque type from an async function that was
+            // annotated with `#[must_use]`.
+            return;
+        }
+
         let ty = cx.typeck_results().expr_ty(&expr);
         let type_permits_lack_of_use = check_must_use_ty(cx, ty, &expr, expr.span, "", "", 1);
 
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 27a57adf964..e0da0096c4e 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -139,7 +139,7 @@ impl CheckAttrVisitor<'_> {
                 sym::collapse_debuginfo => self.check_collapse_debuginfo(attr, span, target),
                 sym::const_trait => self.check_const_trait(attr, span, target),
                 sym::must_not_suspend => self.check_must_not_suspend(&attr, span, target),
-                sym::must_use => self.check_must_use(hir_id, &attr, span, target),
+                sym::must_use => self.check_must_use(hir_id, &attr, target),
                 sym::rustc_pass_by_value => self.check_pass_by_value(&attr, span, target),
                 sym::rustc_allow_incoherent_impl => {
                     self.check_allow_incoherent_impl(&attr, span, target)
@@ -1163,17 +1163,7 @@ impl CheckAttrVisitor<'_> {
     }
 
     /// Warns against some misuses of `#[must_use]`
-    fn check_must_use(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool {
-        let node = self.tcx.hir().get(hir_id);
-        if let Some(kind) = node.fn_kind() && let rustc_hir::IsAsync::Async = kind.asyncness() {
-            self.tcx.emit_spanned_lint(
-                UNUSED_ATTRIBUTES,
-                hir_id,
-                attr.span,
-                errors::MustUseAsync { span }
-            );
-        }
-
+    fn check_must_use(&self, hir_id: HirId, attr: &Attribute, target: Target) -> bool {
         if !matches!(
             target,
             Target::Fn
diff --git a/src/test/ui/lint/unused/unused-async.rs b/src/test/ui/lint/unused/unused-async.rs
index 7d17af11573..eda28dab27f 100644
--- a/src/test/ui/lint/unused/unused-async.rs
+++ b/src/test/ui/lint/unused/unused-async.rs
@@ -1,24 +1,43 @@
 // edition:2018
-// run-pass
-#![allow(dead_code)]
+#![deny(unused_must_use)]
+
 
 #[must_use]
-//~^ WARNING `must_use`
-async fn test() -> i32 {
+async fn foo() -> i32 {
     1
 }
 
+#[must_use]
+fn bar() -> impl std::future::Future<Output=i32> {
+    async {
+        42
+    }
+}
+
+async fn baz() -> i32 {
+    0
+}
 
 struct Wowee {}
 
 impl Wowee {
     #[must_use]
-    //~^ WARNING `must_use`
     async fn test_method() -> i32 {
         1
     }
 }
 
+async fn test() {
+    foo(); //~ ERROR unused return value of `foo` that must be used
+    //~^ ERROR unused implementer of `Future` that must be used
+    foo().await; //~ ERROR unused awaited future returned by `foo` that must be used
+    bar(); //~ ERROR unused return value of `bar` that must be used
+    //~^ ERROR unused implementer of `Future` that must be used
+    bar().await; //~ ERROR unused awaited future returned by `bar` that must be used
+    baz(); //~ ERROR unused implementer of `Future` that must be used
+    baz().await; // ok
+}
+
 /* FIXME(guswynn) update this test when async-fn-in-traits works
 trait Doer {
     #[must_use]
diff --git a/src/test/ui/lint/unused/unused-async.stderr b/src/test/ui/lint/unused/unused-async.stderr
index 6bbc9e2bf00..ae284681720 100644
--- a/src/test/ui/lint/unused/unused-async.stderr
+++ b/src/test/ui/lint/unused/unused-async.stderr
@@ -1,26 +1,55 @@
-warning: `must_use` attribute on `async` functions applies to the anonymous `Future` returned by the function, not the value within
-  --> $DIR/unused-async.rs:5:1
-   |
-LL |   #[must_use]
-   |   ^^^^^^^^^^^
-LL |
-LL | / async fn test() -> i32 {
-LL | |     1
-LL | | }
-   | |_- this attribute does nothing, the `Future`s returned by async functions are already `must_use`
-   |
-   = note: `#[warn(unused_attributes)]` on by default
-
-warning: `must_use` attribute on `async` functions applies to the anonymous `Future` returned by the function, not the value within
-  --> $DIR/unused-async.rs:15:5
-   |
-LL |       #[must_use]
-   |       ^^^^^^^^^^^
-LL |
-LL | /     async fn test_method() -> i32 {
-LL | |         1
-LL | |     }
-   | |_____- this attribute does nothing, the `Future`s returned by async functions are already `must_use`
-
-warning: 2 warnings emitted
+error: unused implementer of `Future` that must be used
+  --> $DIR/unused-async.rs:31:5
+   |
+LL |     foo();
+   |     ^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/unused-async.rs:2:9
+   |
+LL | #![deny(unused_must_use)]
+   |         ^^^^^^^^^^^^^^^
+   = note: futures do nothing unless you `.await` or poll them
+
+error: unused return value of `foo` that must be used
+  --> $DIR/unused-async.rs:31:5
+   |
+LL |     foo();
+   |     ^^^^^
+
+error: unused awaited future returned by `foo` that must be used
+  --> $DIR/unused-async.rs:33:5
+   |
+LL |     foo().await;
+   |     ^^^^^^^^^^^
+
+error: unused implementer of `Future` that must be used
+  --> $DIR/unused-async.rs:34:5
+   |
+LL |     bar();
+   |     ^^^^^
+   |
+   = note: futures do nothing unless you `.await` or poll them
+
+error: unused return value of `bar` that must be used
+  --> $DIR/unused-async.rs:34:5
+   |
+LL |     bar();
+   |     ^^^^^
+
+error: unused awaited future returned by `bar` that must be used
+  --> $DIR/unused-async.rs:36:5
+   |
+LL |     bar().await;
+   |     ^^^^^^^^^^^
+
+error: unused implementer of `Future` that must be used
+  --> $DIR/unused-async.rs:37:5
+   |
+LL |     baz();
+   |     ^^^^^
+   |
+   = note: futures do nothing unless you `.await` or poll them
+
+error: aborting due to 7 previous errors