diff --git a/src/libcore/future/future.rs b/src/libcore/future/future.rs index 0492fd709b8..acca8d7ba15 100644 --- a/src/libcore/future/future.rs +++ b/src/libcore/future/future.rs @@ -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")] diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index d7abdb8ecbe..a6e5bd275ae 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -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; diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index c469d3516e2..14c38ae053d 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -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)) } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 5ae26c4118f..37866bab900 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -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` 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: `::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 { + // `::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: /// /// ``` diff --git a/src/test/ui/async-await/dont-suggest-missing-await.rs b/src/test/ui/async-await/dont-suggest-missing-await.rs new file mode 100644 index 00000000000..d551ef57985 --- /dev/null +++ b/src/test/ui/async-await/dont-suggest-missing-await.rs @@ -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() {} diff --git a/src/test/ui/async-await/dont-suggest-missing-await.stderr b/src/test/ui/async-await/dont-suggest-missing-await.stderr new file mode 100644 index 00000000000..c60b0d1f30e --- /dev/null +++ b/src/test/ui/async-await/dont-suggest-missing-await.stderr @@ -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`. diff --git a/src/test/ui/async-await/suggest-missing-await.fixed b/src/test/ui/async-await/suggest-missing-await.fixed new file mode 100644 index 00000000000..282be368c69 --- /dev/null +++ b/src/test/ui/async-await/suggest-missing-await.fixed @@ -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() {} diff --git a/src/test/ui/async-await/suggest-missing-await.rs b/src/test/ui/async-await/suggest-missing-await.rs new file mode 100644 index 00000000000..36103f050c1 --- /dev/null +++ b/src/test/ui/async-await/suggest-missing-await.rs @@ -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() {} diff --git a/src/test/ui/async-await/suggest-missing-await.stderr b/src/test/ui/async-await/suggest-missing-await.stderr new file mode 100644 index 00000000000..59c20dcfbc9 --- /dev/null +++ b/src/test/ui/async-await/suggest-missing-await.stderr @@ -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`.