about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorcsmoe <csmoe@msn.com>2020-08-16 20:25:22 +0800
committercsmoe <csmoe@msn.com>2020-08-25 14:00:49 +0800
commit1de0dd9531bfae1db458c0d88830a5c09203a100 (patch)
tree08e7825ab56c86dec2c81e755f0dbc9d2ac80c95 /src
parent1d30de6202ea38a204849191cf9723600e522416 (diff)
downloadrust-1de0dd9531bfae1db458c0d88830a5c09203a100.tar.gz
rust-1de0dd9531bfae1db458c0d88830a5c09203a100.zip
suggest await on field access
Diffstat (limited to 'src')
-rw-r--r--src/librustc_typeck/check/expr.rs74
-rw-r--r--src/test/ui/async-await/issue-61076.rs30
-rw-r--r--src/test/ui/async-await/issue-61076.stderr43
3 files changed, 106 insertions, 41 deletions
diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs
index 0e9f64c3596..91f5757adf7 100644
--- a/src/librustc_typeck/check/expr.rs
+++ b/src/librustc_typeck/check/expr.rs
@@ -31,13 +31,14 @@ 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};
 use rustc_span::hygiene::DesugaringKind;
 use rustc_span::source_map::Span;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
-use rustc_trait_selection::traits::{self, ObligationCauseCode};
+use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext};
 
 use std::fmt::Display;
 
@@ -1509,6 +1510,70 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         self.tcx().ty_error()
     }
 
+    fn suggest_await_on_field_access(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        field_ident: Ident,
+        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 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 {
+                    projection_ty = Some(projection_predicate.projection_ty);
+                    break;
+                }
+            }
+        }
+        debug!("suggest_await_on_field_access: projection_ty={:?}", projection_ty);
+
+        let cause = self.misc(expr.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,
+                param_env,
+                projection_ty,
+                cause,
+                0,
+                &mut obligations,
+            );
+            debug!(
+                "suggest_await_on_field_access: normalized_ty={:?}, ty_kind={:?}",
+                self.resolve_vars_if_possible(&normalized_ty),
+                normalized_ty.kind,
+            );
+            if let ty::Adt(def, _) = normalized_ty.kind {
+                if def.non_enum_variant().fields.iter().any(|field| field.ident == field_ident) {
+                    if let Ok(base) = self.tcx.sess.source_map().span_to_snippet(base.span) {
+                        let suggestion = format!("{}.await.{}", base, field_ident);
+                        err.span_suggestion(
+                            expr.span,
+                            "consider await before field access",
+                            suggestion,
+                            Applicability::MaybeIncorrect,
+                        );
+                    }
+                }
+            }
+        }
+    }
+
     fn ban_nonexisting_field(
         &self,
         field: Ident,
@@ -1516,6 +1581,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expr: &'tcx hir::Expr<'tcx>,
         expr_t: Ty<'tcx>,
     ) {
+        debug!(
+            "ban_nonexisting_field: field={:?}, base={:?}, expr={:?}, expr_ty={:?}",
+            field, base, expr, expr_t
+        );
         let mut err = self.no_such_field_err(field.span, field, expr_t);
 
         match expr_t.peel_refs().kind {
@@ -1531,6 +1600,9 @@ 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);
+            }
             _ => {}
         }
 
diff --git a/src/test/ui/async-await/issue-61076.rs b/src/test/ui/async-await/issue-61076.rs
index e1753b28093..aead0ab438f 100644
--- a/src/test/ui/async-await/issue-61076.rs
+++ b/src/test/ui/async-await/issue-61076.rs
@@ -6,14 +6,20 @@ use core::task::{Context, Poll};
 
 struct T;
 
-struct UnionStruct(i32);
+struct Tuple(i32);
 
 struct Struct {
     a: i32
 }
 
-enum Enum {
-    A
+impl Future for Struct {
+    type Output = Struct;
+    fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> { Poll::Pending }
+}
+
+impl Future for Tuple {
+    type Output = Tuple;
+    fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> { Poll::Pending }
 }
 
 impl Future for T {
@@ -33,19 +39,21 @@ async fn bar() -> Result<(), ()> {
     Ok(())
 }
 
+async fn struct_() -> Struct {
+    Struct { a: 1 }
+}
+
+async fn tuple() -> Tuple {
+    Tuple(1i32)
+}
+
 async fn baz() -> Result<(), ()> {
     let t = T;
     t?; //~ ERROR the `?` operator can only be applied to values that implement `std::ops::Try`
 
-    let _: i32 = async {
-        UnionStruct(1i32)
-    }.0; //~ ERROR no field `0`
-
-    let _: i32 = async {
-        Struct { a: 1i32 }
-    }.a; //~ ERROR no field `a`
+    let _: i32 = tuple().0; //~ ERROR no field `0`
 
-    if let Enum::A = async { Enum::A } {} //~ ERROR mismatched type
+    let _: i32 = struct_().a; //~ ERROR no field `a`
 
     Ok(())
 }
diff --git a/src/test/ui/async-await/issue-61076.stderr b/src/test/ui/async-await/issue-61076.stderr
index af176a734e8..df4e2b8e810 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:32:5
+  --> $DIR/issue-61076.rs:38: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:38:5
+  --> $DIR/issue-61076.rs:52:5
    |
 LL |     t?;
    |     ^^
@@ -23,37 +23,22 @@ 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:42:7
+  --> $DIR/issue-61076.rs:54:26
    |
-LL |     }.0;
-   |       ^
+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:46:7
-   |
-LL |     }.a;
-   |       ^
-
-error[E0308]: mismatched types
-  --> $DIR/issue-61076.rs:48:12
-   |
-LL |     A
-   |     - unit variant defined here
-...
-LL |     if let Enum::A = async { Enum::A } {}
-   |            ^^^^^^^         ----------- the expected generator
-   |            |
-   |            expected opaque type, found enum `Enum`
-   | 
-  ::: $SRC_DIR/libcore/future/mod.rs:LL:COL
-   |
-LL | pub const fn from_generator<T>(gen: T) -> impl Future<Output = T::Return>
-   |                                           ------------------------------- the expected opaque type
+  --> $DIR/issue-61076.rs:56:28
    |
-   = note: expected opaque type `impl std::future::Future`
-                     found enum `Enum`
+LL |     let _: i32 = struct_().a;
+   |                  ----------^
+   |                  |
+   |                  help: consider await before field access: `struct_().await.a`
 
-error: aborting due to 5 previous errors
+error: aborting due to 4 previous errors
 
-Some errors have detailed explanations: E0277, E0308, E0609.
+Some errors have detailed explanations: E0277, E0609.
 For more information about an error, try `rustc --explain E0277`.