about summary refs log tree commit diff
diff options
context:
space:
mode:
authorcsmoe <csmoe@msn.com>2020-08-20 15:34:08 +0800
committercsmoe <csmoe@msn.com>2020-08-25 14:02:55 +0800
commit2271b081ebdb10af25aca0206e53931eaed92d3a (patch)
tree2707b6d95db363759f6fd7f8a5e7182b134d3638
parent1de0dd9531bfae1db458c0d88830a5c09203a100 (diff)
downloadrust-2271b081ebdb10af25aca0206e53931eaed92d3a.tar.gz
rust-2271b081ebdb10af25aca0206e53931eaed92d3a.zip
suggest await before method
-rw-r--r--src/librustc_typeck/check/expr.rs16
-rw-r--r--src/librustc_typeck/check/method/suggest.rs71
-rw-r--r--src/test/ui/async-await/issue-61076.rs6
-rw-r--r--src/test/ui/async-await/issue-61076.stderr21
4 files changed, 97 insertions, 17 deletions
diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs
index 91f5757adf7..0f2e4240c01 100644
--- a/src/librustc_typeck/check/expr.rs
+++ b/src/librustc_typeck/check/expr.rs
@@ -31,7 +31,6 @@ use rustc_infer::infer;
 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use rustc_middle::ty;
 use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase};
-use rustc_middle::ty::subst::SubstsRef;
 use rustc_middle::ty::Ty;
 use rustc_middle::ty::TypeFoldable;
 use rustc_middle::ty::{AdtKind, Visibility};
@@ -1517,22 +1516,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         base: &'tcx hir::Expr<'tcx>,
         expr: &'tcx hir::Expr<'tcx>,
         def_id: DefId,
-        substs: SubstsRef<'tcx>,
     ) {
         let param_env = self.tcx().param_env(def_id);
         let future_trait = self.tcx.require_lang_item(lang_items::FutureTraitLangItem, None);
-        let future_trait_ref = ty::TraitRef { def_id: future_trait, substs };
         // Future::Output
-        let future_projection = ty::ProjectionTy::from_ref_and_name(
-            self.tcx,
-            future_trait_ref,
-            Ident::with_dummy_span(sym::Output),
-        );
+        let item_def_id =
+            self.tcx.associated_items(future_trait).in_definition_order().next().unwrap().def_id;
 
         let mut projection_ty = None;
         for (predicate, _) in self.tcx.predicates_of(def_id).predicates {
             if let ty::PredicateAtom::Projection(projection_predicate) = predicate.skip_binders() {
-                if future_projection.item_def_id == projection_predicate.projection_ty.item_def_id {
+                if item_def_id == projection_predicate.projection_ty.item_def_id {
                     projection_ty = Some(projection_predicate.projection_ty);
                     break;
                 }
@@ -1600,8 +1594,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             ty::Param(param_ty) => {
                 self.point_at_param_definition(&mut err, param_ty);
             }
-            ty::Opaque(def_id, subts) => {
-                self.suggest_await_on_field_access(&mut err, field, base, expr, def_id, subts);
+            ty::Opaque(def_id, _) => {
+                self.suggest_await_on_field_access(&mut err, field, base, expr, def_id);
             }
             _ => {}
         }
diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs
index 896bfc07954..8451918568d 100644
--- a/src/librustc_typeck/check/method/suggest.rs
+++ b/src/librustc_typeck/check/method/suggest.rs
@@ -21,6 +21,7 @@ use rustc_span::symbol::{kw, sym, Ident};
 use rustc_span::{source_map, FileName, Span};
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
 use rustc_trait_selection::traits::Obligation;
+use rustc_trait_selection::traits::SelectionContext;
 
 use std::cmp::Ordering;
 
@@ -392,6 +393,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             actual.prefix_string(),
                             ty_str,
                         );
+                        if let Mode::MethodCall = mode {
+                            if let SelfSource::MethodCall(call) = source {
+                                self.suggest_await_before_method(
+                                    &mut err, item_name, actual, call, span,
+                                );
+                            }
+                        }
                         if let Some(span) =
                             tcx.sess.confused_type_with_std_module.borrow().get(&span)
                         {
@@ -854,6 +862,69 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
+    fn suggest_await_before_method(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        item_name: Ident,
+        ty: Ty<'tcx>,
+        call: &hir::Expr<'_>,
+        span: Span,
+    ) {
+        if let ty::Opaque(def_id, _substs) = ty.kind {
+            let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
+            // Future::Output
+            let item_def_id = self
+                .tcx
+                .associated_items(future_trait)
+                .in_definition_order()
+                .next()
+                .unwrap()
+                .def_id;
+
+            let mut projection_ty = None;
+            for (predicate, _) in self.tcx.predicates_of(def_id).predicates {
+                if let ty::PredicateAtom::Projection(projection_predicate) =
+                    predicate.skip_binders()
+                {
+                    if item_def_id == projection_predicate.projection_ty.item_def_id {
+                        projection_ty = Some(projection_predicate.projection_ty);
+                        break;
+                    }
+                }
+            }
+            let cause = self.misc(span);
+            let mut selcx = SelectionContext::new(&self.infcx);
+            let mut obligations = vec![];
+            if let Some(projection_ty) = projection_ty {
+                let normalized_ty = rustc_trait_selection::traits::normalize_projection_type(
+                    &mut selcx,
+                    self.param_env,
+                    projection_ty,
+                    cause,
+                    0,
+                    &mut obligations,
+                );
+                debug!(
+                    "suggest_await_before_method: normalized_ty={:?}, ty_kind={:?}",
+                    self.resolve_vars_if_possible(&normalized_ty),
+                    normalized_ty.kind,
+                );
+                let method_exists = self.method_exists(item_name, normalized_ty, call.hir_id, true);
+                debug!("suggest_await_before_method: is_method_exist={}", method_exists);
+                if let Ok(sp) = self.tcx.sess.source_map().span_to_snippet(span) {
+                    if method_exists {
+                        err.span_suggestion(
+                            span,
+                            "consider await before this method call",
+                            format!("await.{}", sp),
+                            Applicability::MaybeIncorrect,
+                        );
+                    }
+                }
+            }
+        }
+    }
+
     fn suggest_use_candidates(
         &self,
         err: &mut DiagnosticBuilder<'_>,
diff --git a/src/test/ui/async-await/issue-61076.rs b/src/test/ui/async-await/issue-61076.rs
index aead0ab438f..743f1959828 100644
--- a/src/test/ui/async-await/issue-61076.rs
+++ b/src/test/ui/async-await/issue-61076.rs
@@ -12,6 +12,10 @@ struct Struct {
     a: i32
 }
 
+impl Struct {
+    fn method(&self) {}
+}
+
 impl Future for Struct {
     type Output = Struct;
     fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> { Poll::Pending }
@@ -55,6 +59,8 @@ async fn baz() -> Result<(), ()> {
 
     let _: i32 = struct_().a; //~ ERROR no field `a`
 
+    struct_().method(); //~ ERROR no method named
+
     Ok(())
 }
 
diff --git a/src/test/ui/async-await/issue-61076.stderr b/src/test/ui/async-await/issue-61076.stderr
index df4e2b8e810..692117a06b0 100644
--- a/src/test/ui/async-await/issue-61076.stderr
+++ b/src/test/ui/async-await/issue-61076.stderr
@@ -1,5 +1,5 @@
 error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try`
-  --> $DIR/issue-61076.rs:38:5
+  --> $DIR/issue-61076.rs:42:5
    |
 LL |     foo()?;
    |     ^^^^^^
@@ -11,7 +11,7 @@ LL |     foo()?;
    = note: required by `std::ops::Try::into_result`
 
 error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try`
-  --> $DIR/issue-61076.rs:52:5
+  --> $DIR/issue-61076.rs:56:5
    |
 LL |     t?;
    |     ^^
@@ -23,7 +23,7 @@ LL |     t?;
    = note: required by `std::ops::Try::into_result`
 
 error[E0609]: no field `0` on type `impl std::future::Future`
-  --> $DIR/issue-61076.rs:54:26
+  --> $DIR/issue-61076.rs:58:26
    |
 LL |     let _: i32 = tuple().0;
    |                  --------^
@@ -31,14 +31,23 @@ LL |     let _: i32 = tuple().0;
    |                  help: consider await before field access: `tuple().await.0`
 
 error[E0609]: no field `a` on type `impl std::future::Future`
-  --> $DIR/issue-61076.rs:56:28
+  --> $DIR/issue-61076.rs:60:28
    |
 LL |     let _: i32 = struct_().a;
    |                  ----------^
    |                  |
    |                  help: consider await before field access: `struct_().await.a`
 
-error: aborting due to 4 previous errors
+error[E0599]: no method named `method` found for opaque type `impl std::future::Future` in the current scope
+  --> $DIR/issue-61076.rs:62:15
+   |
+LL |     struct_().method();
+   |               ^^^^^^
+   |               |
+   |               method not found in `impl std::future::Future`
+   |               help: consider await before this method call: `await.method`
+
+error: aborting due to 5 previous errors
 
-Some errors have detailed explanations: E0277, E0609.
+Some errors have detailed explanations: E0277, E0599, E0609.
 For more information about an error, try `rustc --explain E0277`.