Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add suggestion for missing .await keyword #62067

Merged
merged 1 commit into from
Jun 28, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/libcore/future/future.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down
1 change: 1 addition & 0 deletions src/librustc/middle/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions src/librustc_typeck/check/demand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}
Expand Down
66 changes: 66 additions & 0 deletions src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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:
///
/// ```
Expand Down
21 changes: 21 additions & 0 deletions src/test/ui/async-await/dont-suggest-missing-await.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 12 additions & 0 deletions src/test/ui/async-await/dont-suggest-missing-await.stderr
Original file line number Diff line number Diff line change
@@ -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 changes: 32 additions & 0 deletions src/test/ui/async-await/suggest-missing-await.fixed
Original file line number Diff line number Diff line change
@@ -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 changes: 32 additions & 0 deletions src/test/ui/async-await/suggest-missing-await.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 27 additions & 0 deletions src/test/ui/async-await/suggest-missing-await.stderr
Original file line number Diff line number Diff line change
@@ -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`.