about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNathan Corbyn <me@nathancorbyn.com>2019-06-19 21:55:18 +0100
committerNathan Corbyn <me@nathancorbyn.com>2019-06-27 13:56:55 +0100
commit88194200e57c90ba0fa7b725d63ff4de28e71bbb (patch)
tree75a61129eb0e2d57719737c2dc94d3c237565871
parenta6b5d229c1ddd7659af82109377aa8a3568f68a1 (diff)
downloadrust-88194200e57c90ba0fa7b725d63ff4de28e71bbb.tar.gz
rust-88194200e57c90ba0fa7b725d63ff4de28e71bbb.zip
Add suggestion for missing `.await` keyword
-rw-r--r--src/libcore/future/future.rs1
-rw-r--r--src/librustc/middle/lang_items.rs1
-rw-r--r--src/librustc_typeck/check/demand.rs1
-rw-r--r--src/librustc_typeck/check/mod.rs66
-rw-r--r--src/test/ui/async-await/dont-suggest-missing-await.rs21
-rw-r--r--src/test/ui/async-await/dont-suggest-missing-await.stderr12
-rw-r--r--src/test/ui/async-await/suggest-missing-await.fixed32
-rw-r--r--src/test/ui/async-await/suggest-missing-await.rs32
-rw-r--r--src/test/ui/async-await/suggest-missing-await.stderr27
9 files changed, 193 insertions, 0 deletions
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<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:
     ///
     /// ```
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`.