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.
This commit is contained in:
parent
50bb7a40e1
commit
243496e129
4 changed files with 89 additions and 39 deletions
|
@ -9,7 +9,7 @@ use rustc_hir::def::{DefKind, Res};
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_infer::traits::util::elaborate_predicates_with_span;
|
use rustc_infer::traits::util::elaborate_predicates_with_span;
|
||||||
use rustc_middle::ty::adjustment;
|
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::Symbol;
|
||||||
use rustc_span::symbol::{kw, sym};
|
use rustc_span::symbol::{kw, sym};
|
||||||
use rustc_span::{BytePos, Span};
|
use rustc_span::{BytePos, Span};
|
||||||
|
@ -93,6 +93,18 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
|
||||||
return;
|
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 ty = cx.typeck_results().expr_ty(&expr);
|
||||||
let type_permits_lack_of_use = check_must_use_ty(cx, ty, &expr, expr.span, "", "", 1);
|
let type_permits_lack_of_use = check_must_use_ty(cx, ty, &expr, expr.span, "", "", 1);
|
||||||
|
|
||||||
|
|
|
@ -139,7 +139,7 @@ impl CheckAttrVisitor<'_> {
|
||||||
sym::collapse_debuginfo => self.check_collapse_debuginfo(attr, span, target),
|
sym::collapse_debuginfo => self.check_collapse_debuginfo(attr, span, target),
|
||||||
sym::const_trait => self.check_const_trait(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_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_pass_by_value => self.check_pass_by_value(&attr, span, target),
|
||||||
sym::rustc_allow_incoherent_impl => {
|
sym::rustc_allow_incoherent_impl => {
|
||||||
self.check_allow_incoherent_impl(&attr, span, target)
|
self.check_allow_incoherent_impl(&attr, span, target)
|
||||||
|
@ -1163,17 +1163,7 @@ impl CheckAttrVisitor<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Warns against some misuses of `#[must_use]`
|
/// Warns against some misuses of `#[must_use]`
|
||||||
fn check_must_use(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool {
|
fn check_must_use(&self, hir_id: HirId, attr: &Attribute, 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 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if !matches!(
|
if !matches!(
|
||||||
target,
|
target,
|
||||||
Target::Fn
|
Target::Fn
|
||||||
|
|
|
@ -1,24 +1,43 @@
|
||||||
// edition:2018
|
// edition:2018
|
||||||
// run-pass
|
#![deny(unused_must_use)]
|
||||||
#![allow(dead_code)]
|
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
//~^ WARNING `must_use`
|
async fn foo() -> i32 {
|
||||||
async fn test() -> i32 {
|
|
||||||
1
|
1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
fn bar() -> impl std::future::Future<Output=i32> {
|
||||||
|
async {
|
||||||
|
42
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn baz() -> i32 {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
struct Wowee {}
|
struct Wowee {}
|
||||||
|
|
||||||
impl Wowee {
|
impl Wowee {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
//~^ WARNING `must_use`
|
|
||||||
async fn test_method() -> i32 {
|
async fn test_method() -> i32 {
|
||||||
1
|
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
|
/* FIXME(guswynn) update this test when async-fn-in-traits works
|
||||||
trait Doer {
|
trait Doer {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
|
|
@ -1,26 +1,55 @@
|
||||||
warning: `must_use` attribute on `async` functions applies to the anonymous `Future` returned by the function, not the value within
|
error: unused implementer of `Future` that must be used
|
||||||
--> $DIR/unused-async.rs:5:1
|
--> $DIR/unused-async.rs:31:5
|
||||||
|
|
|
|
||||||
LL | #[must_use]
|
LL | foo();
|
||||||
| ^^^^^^^^^^^
|
| ^^^^^
|
||||||
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
|
note: the lint level is defined here
|
||||||
|
--> $DIR/unused-async.rs:2:9
|
||||||
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 | #![deny(unused_must_use)]
|
||||||
| ^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^
|
||||||
LL |
|
= note: futures do nothing unless you `.await` or poll them
|
||||||
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 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
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue