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 { 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` -Some errors have detailed explanations: E0277, E0609. +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0277, E0599, E0609. For more information about an error, try `rustc --explain E0277`.