Skip to content

Commit 86ffeef

Browse files
committed
Add suggestion for missing .await keyword
1 parent 3cc3486 commit 86ffeef

File tree

9 files changed

+193
-0
lines changed

9 files changed

+193
-0
lines changed

src/libcore/future/future.rs

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ use crate::task::{Context, Poll};
2525
#[doc(spotlight)]
2626
#[must_use = "futures do nothing unless you `.await` or poll them"]
2727
#[stable(feature = "futures_api", since = "1.36.0")]
28+
#[cfg_attr(not(bootstrap), lang = "future_trait")]
2829
pub trait Future {
2930
/// The type of value produced on completion.
3031
#[stable(feature = "futures_api", since = "1.36.0")]

src/librustc/middle/lang_items.rs

+1
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,7 @@ language_item_table! {
320320
FnMutTraitLangItem, "fn_mut", fn_mut_trait, Target::Trait;
321321
FnOnceTraitLangItem, "fn_once", fn_once_trait, Target::Trait;
322322

323+
FutureTraitLangItem, "future_trait", future_trait, Target::Trait;
323324
GeneratorStateLangItem, "generator_state", gen_state, Target::Enum;
324325
GeneratorTraitLangItem, "generator", gen_trait, Target::Trait;
325326
UnpinTraitLangItem, "unpin", unpin_trait, Target::Trait;

src/librustc_typeck/check/demand.rs

+1
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
127127

128128
self.suggest_compatible_variants(&mut err, expr, expected, expr_ty);
129129
self.suggest_ref_or_into(&mut err, expr, expected, expr_ty);
130+
self.suggest_missing_await(&mut err, expr, expected, expr_ty);
130131

131132
(expected, Some(err))
132133
}

src/librustc_typeck/check/mod.rs

+66
Original file line numberDiff line numberDiff line change
@@ -3913,6 +3913,72 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
39133913
}
39143914
}
39153915

3916+
/// A possible error is to forget to add `.await` when using futures:
3917+
///
3918+
/// ```
3919+
/// #![feature(async_await)]
3920+
///
3921+
/// async fn make_u32() -> u32 {
3922+
/// 22
3923+
/// }
3924+
///
3925+
/// fn take_u32(x: u32) {}
3926+
///
3927+
/// async fn foo() {
3928+
/// let x = make_u32();
3929+
/// take_u32(x);
3930+
/// }
3931+
/// ```
3932+
///
3933+
/// This routine checks if the found type `T` implements `Future<Output=U>` where `U` is the
3934+
/// expected type. If this is the case, and we are inside of an async body, it suggests adding
3935+
/// `.await` to the tail of the expression.
3936+
fn suggest_missing_await(
3937+
&self,
3938+
err: &mut DiagnosticBuilder<'tcx>,
3939+
expr: &hir::Expr,
3940+
expected: Ty<'tcx>,
3941+
found: Ty<'tcx>,
3942+
) {
3943+
// `.await` is not permitted outside of `async` bodies, so don't bother to suggest if the
3944+
// body isn't `async`.
3945+
let item_id = self.tcx().hir().get_parent_node_by_hir_id(self.body_id);
3946+
if let Some(body_id) = self.tcx().hir().maybe_body_owned_by(item_id) {
3947+
let body = self.tcx().hir().body(body_id);
3948+
if let Some(hir::GeneratorKind::Async) = body.generator_kind {
3949+
let sp = expr.span;
3950+
// Check for `Future` implementations by constructing a predicate to
3951+
// prove: `<T as Future>::Output == U`
3952+
let future_trait = self.tcx.lang_items().future_trait().unwrap();
3953+
let item_def_id = self.tcx.associated_items(future_trait).next().unwrap().def_id;
3954+
let predicate = ty::Predicate::Projection(ty::Binder::bind(ty::ProjectionPredicate {
3955+
// `<T as Future>::Output`
3956+
projection_ty: ty::ProjectionTy {
3957+
// `T`
3958+
substs: self.tcx.mk_substs_trait(
3959+
found,
3960+
self.fresh_substs_for_item(sp, item_def_id)
3961+
),
3962+
// `Future::Output`
3963+
item_def_id,
3964+
},
3965+
ty: expected,
3966+
}));
3967+
let obligation = traits::Obligation::new(self.misc(sp), self.param_env, predicate);
3968+
if self.infcx.predicate_may_hold(&obligation) {
3969+
if let Ok(code) = self.sess().source_map().span_to_snippet(sp) {
3970+
err.span_suggestion(
3971+
sp,
3972+
"consider using `.await` here",
3973+
format!("{}.await", code),
3974+
Applicability::MaybeIncorrect,
3975+
);
3976+
}
3977+
}
3978+
}
3979+
}
3980+
}
3981+
39163982
/// A common error is to add an extra semicolon:
39173983
///
39183984
/// ```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// edition:2018
2+
3+
// This test ensures we don't make the suggestion in bodies that aren't `async`.
4+
5+
#![feature(async_await)]
6+
7+
fn take_u32(x: u32) {}
8+
9+
async fn make_u32() -> u32 {
10+
22
11+
}
12+
13+
async fn dont_suggest_await_in_closure() {
14+
|| {
15+
let x = make_u32();
16+
take_u32(x)
17+
//~^ ERROR mismatched types [E0308]
18+
};
19+
}
20+
21+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/dont-suggest-missing-await.rs:16:18
3+
|
4+
LL | take_u32(x)
5+
| ^ expected u32, found opaque type
6+
|
7+
= note: expected type `u32`
8+
found type `impl std::future::Future`
9+
10+
error: aborting due to previous error
11+
12+
For more information about this error, try `rustc --explain E0308`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// edition:2018
2+
// run-rustfix
3+
4+
#![feature(async_await)]
5+
6+
fn take_u32(_x: u32) {}
7+
8+
async fn make_u32() -> u32 {
9+
22
10+
}
11+
12+
#[allow(unused)]
13+
async fn suggest_await_in_async_fn() {
14+
let x = make_u32();
15+
take_u32(x.await)
16+
//~^ ERROR mismatched types [E0308]
17+
//~| HELP consider using `.await` here
18+
//~| SUGGESTION x.await
19+
}
20+
21+
#[allow(unused)]
22+
async fn suggest_await_in_async_closure() {
23+
async || {
24+
let x = make_u32();
25+
take_u32(x.await)
26+
//~^ ERROR mismatched types [E0308]
27+
//~| HELP consider using `.await` here
28+
//~| SUGGESTION x.await
29+
};
30+
}
31+
32+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// edition:2018
2+
// run-rustfix
3+
4+
#![feature(async_await)]
5+
6+
fn take_u32(_x: u32) {}
7+
8+
async fn make_u32() -> u32 {
9+
22
10+
}
11+
12+
#[allow(unused)]
13+
async fn suggest_await_in_async_fn() {
14+
let x = make_u32();
15+
take_u32(x)
16+
//~^ ERROR mismatched types [E0308]
17+
//~| HELP consider using `.await` here
18+
//~| SUGGESTION x.await
19+
}
20+
21+
#[allow(unused)]
22+
async fn suggest_await_in_async_closure() {
23+
async || {
24+
let x = make_u32();
25+
take_u32(x)
26+
//~^ ERROR mismatched types [E0308]
27+
//~| HELP consider using `.await` here
28+
//~| SUGGESTION x.await
29+
};
30+
}
31+
32+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/suggest-missing-await.rs:15:14
3+
|
4+
LL | take_u32(x)
5+
| ^
6+
| |
7+
| expected u32, found opaque type
8+
| help: consider using `.await` here: `x.await`
9+
|
10+
= note: expected type `u32`
11+
found type `impl std::future::Future`
12+
13+
error[E0308]: mismatched types
14+
--> $DIR/suggest-missing-await.rs:25:18
15+
|
16+
LL | take_u32(x)
17+
| ^
18+
| |
19+
| expected u32, found opaque type
20+
| help: consider using `.await` here: `x.await`
21+
|
22+
= note: expected type `u32`
23+
found type `impl std::future::Future`
24+
25+
error: aborting due to 2 previous errors
26+
27+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)