Add suggestion for missing .await
keyword
This commit is contained in:
parent
a6b5d229c1
commit
88194200e5
@ -25,6 +25,7 @@ use crate::task::{Context, Poll};
|
||||
#[doc(spotlight)]
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
#[stable(feature = "futures_api", since = "1.36.0")]
|
||||
#[cfg_attr(not(bootstrap), lang = "future_trait")]
|
||||
pub trait Future {
|
||||
/// The type of value produced on completion.
|
||||
#[stable(feature = "futures_api", since = "1.36.0")]
|
||||
|
@ -320,6 +320,7 @@ language_item_table! {
|
||||
FnMutTraitLangItem, "fn_mut", fn_mut_trait, Target::Trait;
|
||||
FnOnceTraitLangItem, "fn_once", fn_once_trait, Target::Trait;
|
||||
|
||||
FutureTraitLangItem, "future_trait", future_trait, Target::Trait;
|
||||
GeneratorStateLangItem, "generator_state", gen_state, Target::Enum;
|
||||
GeneratorTraitLangItem, "generator", gen_trait, Target::Trait;
|
||||
UnpinTraitLangItem, "unpin", unpin_trait, Target::Trait;
|
||||
|
@ -127,6 +127,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
|
||||
self.suggest_compatible_variants(&mut err, expr, expected, expr_ty);
|
||||
self.suggest_ref_or_into(&mut err, expr, expected, expr_ty);
|
||||
self.suggest_missing_await(&mut err, expr, expected, expr_ty);
|
||||
|
||||
(expected, Some(err))
|
||||
}
|
||||
|
@ -3932,6 +3932,72 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// A possible error is to forget to add `.await` when using futures:
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(async_await)]
|
||||
///
|
||||
/// async fn make_u32() -> u32 {
|
||||
/// 22
|
||||
/// }
|
||||
///
|
||||
/// fn take_u32(x: u32) {}
|
||||
///
|
||||
/// async fn foo() {
|
||||
/// let x = make_u32();
|
||||
/// take_u32(x);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// This routine checks if the found type `T` implements `Future<Output=U>` where `U` is the
|
||||
/// expected type. If this is the case, and we are inside of an async body, it suggests adding
|
||||
/// `.await` to the tail of the expression.
|
||||
fn suggest_missing_await(
|
||||
&self,
|
||||
err: &mut DiagnosticBuilder<'tcx>,
|
||||
expr: &hir::Expr,
|
||||
expected: Ty<'tcx>,
|
||||
found: Ty<'tcx>,
|
||||
) {
|
||||
// `.await` is not permitted outside of `async` bodies, so don't bother to suggest if the
|
||||
// body isn't `async`.
|
||||
let item_id = self.tcx().hir().get_parent_node(self.body_id);
|
||||
if let Some(body_id) = self.tcx().hir().maybe_body_owned_by(item_id) {
|
||||
let body = self.tcx().hir().body(body_id);
|
||||
if let Some(hir::GeneratorKind::Async) = body.generator_kind {
|
||||
let sp = expr.span;
|
||||
// Check for `Future` implementations by constructing a predicate to
|
||||
// prove: `<T as Future>::Output == U`
|
||||
let future_trait = self.tcx.lang_items().future_trait().unwrap();
|
||||
let item_def_id = self.tcx.associated_items(future_trait).next().unwrap().def_id;
|
||||
let predicate = ty::Predicate::Projection(ty::Binder::bind(ty::ProjectionPredicate {
|
||||
// `<T as Future>::Output`
|
||||
projection_ty: ty::ProjectionTy {
|
||||
// `T`
|
||||
substs: self.tcx.mk_substs_trait(
|
||||
found,
|
||||
self.fresh_substs_for_item(sp, item_def_id)
|
||||
),
|
||||
// `Future::Output`
|
||||
item_def_id,
|
||||
},
|
||||
ty: expected,
|
||||
}));
|
||||
let obligation = traits::Obligation::new(self.misc(sp), self.param_env, predicate);
|
||||
if self.infcx.predicate_may_hold(&obligation) {
|
||||
if let Ok(code) = self.sess().source_map().span_to_snippet(sp) {
|
||||
err.span_suggestion(
|
||||
sp,
|
||||
"consider using `.await` here",
|
||||
format!("{}.await", code),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A common error is to add an extra semicolon:
|
||||
///
|
||||
/// ```
|
||||
|
21
src/test/ui/async-await/dont-suggest-missing-await.rs
Normal file
21
src/test/ui/async-await/dont-suggest-missing-await.rs
Normal file
@ -0,0 +1,21 @@
|
||||
// edition:2018
|
||||
|
||||
// This test ensures we don't make the suggestion in bodies that aren't `async`.
|
||||
|
||||
#![feature(async_await)]
|
||||
|
||||
fn take_u32(x: u32) {}
|
||||
|
||||
async fn make_u32() -> u32 {
|
||||
22
|
||||
}
|
||||
|
||||
async fn dont_suggest_await_in_closure() {
|
||||
|| {
|
||||
let x = make_u32();
|
||||
take_u32(x)
|
||||
//~^ ERROR mismatched types [E0308]
|
||||
};
|
||||
}
|
||||
|
||||
fn main() {}
|
12
src/test/ui/async-await/dont-suggest-missing-await.stderr
Normal file
12
src/test/ui/async-await/dont-suggest-missing-await.stderr
Normal file
@ -0,0 +1,12 @@
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/dont-suggest-missing-await.rs:16:18
|
||||
|
|
||||
LL | take_u32(x)
|
||||
| ^ expected u32, found opaque type
|
||||
|
|
||||
= note: expected type `u32`
|
||||
found type `impl std::future::Future`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
32
src/test/ui/async-await/suggest-missing-await.fixed
Normal file
32
src/test/ui/async-await/suggest-missing-await.fixed
Normal file
@ -0,0 +1,32 @@
|
||||
// edition:2018
|
||||
// run-rustfix
|
||||
|
||||
#![feature(async_await)]
|
||||
|
||||
fn take_u32(_x: u32) {}
|
||||
|
||||
async fn make_u32() -> u32 {
|
||||
22
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
async fn suggest_await_in_async_fn() {
|
||||
let x = make_u32();
|
||||
take_u32(x.await)
|
||||
//~^ ERROR mismatched types [E0308]
|
||||
//~| HELP consider using `.await` here
|
||||
//~| SUGGESTION x.await
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
async fn suggest_await_in_async_closure() {
|
||||
async || {
|
||||
let x = make_u32();
|
||||
take_u32(x.await)
|
||||
//~^ ERROR mismatched types [E0308]
|
||||
//~| HELP consider using `.await` here
|
||||
//~| SUGGESTION x.await
|
||||
};
|
||||
}
|
||||
|
||||
fn main() {}
|
32
src/test/ui/async-await/suggest-missing-await.rs
Normal file
32
src/test/ui/async-await/suggest-missing-await.rs
Normal file
@ -0,0 +1,32 @@
|
||||
// edition:2018
|
||||
// run-rustfix
|
||||
|
||||
#![feature(async_await)]
|
||||
|
||||
fn take_u32(_x: u32) {}
|
||||
|
||||
async fn make_u32() -> u32 {
|
||||
22
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
async fn suggest_await_in_async_fn() {
|
||||
let x = make_u32();
|
||||
take_u32(x)
|
||||
//~^ ERROR mismatched types [E0308]
|
||||
//~| HELP consider using `.await` here
|
||||
//~| SUGGESTION x.await
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
async fn suggest_await_in_async_closure() {
|
||||
async || {
|
||||
let x = make_u32();
|
||||
take_u32(x)
|
||||
//~^ ERROR mismatched types [E0308]
|
||||
//~| HELP consider using `.await` here
|
||||
//~| SUGGESTION x.await
|
||||
};
|
||||
}
|
||||
|
||||
fn main() {}
|
27
src/test/ui/async-await/suggest-missing-await.stderr
Normal file
27
src/test/ui/async-await/suggest-missing-await.stderr
Normal file
@ -0,0 +1,27 @@
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/suggest-missing-await.rs:15:14
|
||||
|
|
||||
LL | take_u32(x)
|
||||
| ^
|
||||
| |
|
||||
| expected u32, found opaque type
|
||||
| help: consider using `.await` here: `x.await`
|
||||
|
|
||||
= note: expected type `u32`
|
||||
found type `impl std::future::Future`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/suggest-missing-await.rs:25:18
|
||||
|
|
||||
LL | take_u32(x)
|
||||
| ^
|
||||
| |
|
||||
| expected u32, found opaque type
|
||||
| help: consider using `.await` here: `x.await`
|
||||
|
|
||||
= note: expected type `u32`
|
||||
found type `impl std::future::Future`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
Loading…
x
Reference in New Issue
Block a user