From 1d30de6202ea38a204849191cf9723600e522416 Mon Sep 17 00:00:00 2001 From: csmoe Date: Sat, 30 May 2020 22:57:12 +0800 Subject: [PATCH 1/5] append more test cases for issue 61076 --- src/test/ui/async-await/issue-61076.rs | 22 ++++++++++++ src/test/ui/async-await/issue-61076.stderr | 40 +++++++++++++++++++--- 2 files changed, 58 insertions(+), 4 deletions(-) diff --git a/src/test/ui/async-await/issue-61076.rs b/src/test/ui/async-await/issue-61076.rs index 13b45df64eabe..e1753b280935d 100644 --- a/src/test/ui/async-await/issue-61076.rs +++ b/src/test/ui/async-await/issue-61076.rs @@ -6,6 +6,16 @@ use core::task::{Context, Poll}; struct T; +struct UnionStruct(i32); + +struct Struct { + a: i32 +} + +enum Enum { + A +} + impl Future for T { type Output = Result<(), ()>; @@ -26,7 +36,19 @@ async fn bar() -> Result<(), ()> { 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` + + if let Enum::A = async { Enum::A } {} //~ ERROR mismatched type + Ok(()) } + fn main() {} diff --git a/src/test/ui/async-await/issue-61076.stderr b/src/test/ui/async-await/issue-61076.stderr index e71f4e7136dad..af176a734e809 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:22:5 + --> $DIR/issue-61076.rs:32: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:28:5 + --> $DIR/issue-61076.rs:38:5 | LL | t?; | ^^ @@ -22,6 +22,38 @@ LL | t?; = help: the trait `std::ops::Try` is not implemented for `T` = note: required by `std::ops::Try::into_result` -error: aborting due to 2 previous errors +error[E0609]: no field `0` on type `impl std::future::Future` + --> $DIR/issue-61076.rs:42:7 + | +LL | }.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(gen: T) -> impl Future + | ------------------------------- the expected opaque type + | + = note: expected opaque type `impl std::future::Future` + found enum `Enum` + +error: aborting due to 5 previous errors -For more information about this error, try `rustc --explain E0277`. +Some errors have detailed explanations: E0277, E0308, E0609. +For more information about an error, try `rustc --explain E0277`. From 1de0dd9531bfae1db458c0d88830a5c09203a100 Mon Sep 17 00:00:00 2001 From: csmoe Date: Sun, 16 Aug 2020 20:25:22 +0800 Subject: [PATCH 2/5] suggest await on field access --- src/librustc_typeck/check/expr.rs | 74 +++++++++++++++++++++- src/test/ui/async-await/issue-61076.rs | 30 +++++---- src/test/ui/async-await/issue-61076.stderr | 43 ++++--------- 3 files changed, 106 insertions(+), 41 deletions(-) diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs index 0e9f64c359690..91f5757adf7c7 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 e1753b280935d..aead0ab438f20 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 { Poll::Pending } +} + +impl Future for Tuple { + type Output = Tuple; + fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll { 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 af176a734e809..df4e2b8e810dd 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(gen: T) -> impl Future - | ------------------------------- 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`. From 2271b081ebdb10af25aca0206e53931eaed92d3a Mon Sep 17 00:00:00 2001 From: csmoe Date: Thu, 20 Aug 2020 15:34:08 +0800 Subject: [PATCH 3/5] suggest await before method --- src/librustc_typeck/check/expr.rs | 16 ++--- src/librustc_typeck/check/method/suggest.rs | 71 +++++++++++++++++++++ src/test/ui/async-await/issue-61076.rs | 6 ++ src/test/ui/async-await/issue-61076.stderr | 21 ++++-- 4 files changed, 97 insertions(+), 17 deletions(-) diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs index 91f5757adf7c7..0f2e4240c01dd 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 896bfc0795434..8451918568d7d 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 aead0ab438f20..743f19598283a 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 df4e2b8e810dd..692117a06b06c 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`. From 8ee206a80dab320ee76a2aa9324c587368df3921 Mon Sep 17 00:00:00 2001 From: csmoe Date: Thu, 20 Aug 2020 18:42:08 +0800 Subject: [PATCH 4/5] suggest await on unexpected types --- .../infer/error_reporting/mod.rs | 70 +++++++++++++++++++ src/librustc_typeck/check/_match.rs | 4 +- src/librustc_typeck/check/expr.rs | 17 ++--- src/librustc_typeck/check/method/suggest.rs | 16 ++--- src/test/ui/async-await/issue-61076.rs | 5 ++ src/test/ui/async-await/issue-61076.stderr | 46 +++++++++--- 6 files changed, 126 insertions(+), 32 deletions(-) diff --git a/src/librustc_infer/infer/error_reporting/mod.rs b/src/librustc_infer/infer/error_reporting/mod.rs index 8212958510a6f..35b2d7e846839 100644 --- a/src/librustc_infer/infer/error_reporting/mod.rs +++ b/src/librustc_infer/infer/error_reporting/mod.rs @@ -50,6 +50,7 @@ use super::region_constraints::GenericKind; use super::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TypeTrace, ValuePairs}; use crate::infer; +use crate::infer::OriginalQueryValues; use crate::traits::error_reporting::report_object_safety_error; use crate::traits::{ IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode, @@ -60,8 +61,10 @@ use rustc_errors::{pluralize, struct_span_err}; use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString}; use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_hir::lang_items::LangItem; use rustc_hir::{Item, ItemKind, Node}; use rustc_middle::ty::error::TypeError; +use rustc_middle::ty::ParamEnvAnd; use rustc_middle::ty::{ self, subst::{Subst, SubstsRef}, @@ -1529,6 +1532,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { }; if let Some(exp_found) = exp_found { self.suggest_as_ref_where_appropriate(span, &exp_found, diag); + self.suggest_await_on_expect_found(cause, span, &exp_found, diag); } // In some (most?) cases cause.body_id points to actual body, but in some cases @@ -1547,6 +1551,72 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { self.note_error_origin(diag, cause, exp_found); } + fn suggest_await_on_expect_found( + &self, + cause: &ObligationCause<'tcx>, + exp_span: Span, + exp_found: &ty::error::ExpectedFound>, + diag: &mut DiagnosticBuilder<'tcx>, + ) { + debug!( + "suggest_await_on_expect_found: exp_span={:?}, expected_ty={:?}, found_ty={:?}", + exp_span, exp_found.expected, exp_found.found + ); + + if let ty::Opaque(def_id, _) = exp_found.expected.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; + } + } + } + if let Some(projection_ty) = projection_ty { + let projection_query = self.canonicalize_query( + &ParamEnvAnd { param_env: self.tcx.param_env(def_id), value: projection_ty }, + &mut OriginalQueryValues::default(), + ); + if let Ok(resp) = self.tcx.normalize_projection_ty(projection_query) { + let normalized_ty = resp.value.value.normalized_ty; + debug!("suggest_await_on_expect_found: normalized={:?}", normalized_ty); + if ty::TyS::same_type(normalized_ty, exp_found.found) { + let span = if let ObligationCauseCode::Pattern { + span, + origin_expr: _, + root_ty: _, + } = cause.code + { + // scrutinee's span + span.unwrap_or(exp_span) + } else { + exp_span + }; + diag.span_suggestion_verbose( + span.shrink_to_hi(), + "consider awaiting on the future", + ".await".to_string(), + Applicability::MaybeIncorrect, + ); + } + } + } + } + } + /// When encountering a case where `.as_ref()` on a `Result` or `Option` would be appropriate, /// suggests it. fn suggest_as_ref_where_appropriate( diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index 40088bc069005..afd4413069ee1 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -28,7 +28,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; // Type check the descriminant and get its type. - let scrut_ty = if force_scrutinee_bool { + let scrutinee_ty = if force_scrutinee_bool { // Here we want to ensure: // // 1. That default match bindings are *not* accepted in the condition of an @@ -55,7 +55,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // #55810: Type check patterns first so we get types for all bindings. for arm in arms { - self.check_pat_top(&arm.pat, scrut_ty, Some(scrut.span), true); + self.check_pat_top(&arm.pat, scrutinee_ty, Some(scrut.span), true); } // Now typecheck the blocks. diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs index 0f2e4240c01dd..702dc30957ccf 100644 --- a/src/librustc_typeck/check/expr.rs +++ b/src/librustc_typeck/check/expr.rs @@ -1518,7 +1518,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { def_id: DefId, ) { let param_env = self.tcx().param_env(def_id); - let future_trait = self.tcx.require_lang_item(lang_items::FutureTraitLangItem, None); + 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; @@ -1554,15 +1554,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); 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, - ); - } + err.span_suggestion_verbose( + base.span.shrink_to_hi(), + "consider awaiting before field access", + ".await".to_string(), + Applicability::MaybeIncorrect, + ); } } } diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs index 8451918568d7d..3dd4c7c143967 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -911,15 +911,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); 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, - ); - } + if method_exists { + err.span_suggestion_verbose( + span.shrink_to_lo(), + "consider awaiting before this method call", + "await.".to_string(), + Applicability::MaybeIncorrect, + ); } } } diff --git a/src/test/ui/async-await/issue-61076.rs b/src/test/ui/async-await/issue-61076.rs index 743f19598283a..e383a9126f7bc 100644 --- a/src/test/ui/async-await/issue-61076.rs +++ b/src/test/ui/async-await/issue-61076.rs @@ -64,5 +64,10 @@ async fn baz() -> Result<(), ()> { Ok(()) } +async fn match_() { + match tuple() { + Tuple(_) => {} //~ ERROR mismatched types + } +} fn main() {} diff --git a/src/test/ui/async-await/issue-61076.stderr b/src/test/ui/async-await/issue-61076.stderr index 692117a06b06c..69b6e8c3cf569 100644 --- a/src/test/ui/async-await/issue-61076.stderr +++ b/src/test/ui/async-await/issue-61076.stderr @@ -26,28 +26,52 @@ error[E0609]: no field `0` on type `impl std::future::Future` --> $DIR/issue-61076.rs:58:26 | LL | let _: i32 = tuple().0; - | --------^ - | | - | help: consider await before field access: `tuple().await.0` + | ^ + | +help: consider awaiting before field access + | +LL | let _: i32 = tuple().await.0; + | ^^^^^^ error[E0609]: no field `a` on type `impl std::future::Future` --> $DIR/issue-61076.rs:60:28 | LL | let _: i32 = struct_().a; - | ----------^ - | | - | help: consider await before field access: `struct_().await.a` + | ^ + | +help: consider awaiting before field access + | +LL | let _: i32 = struct_().await.a; + | ^^^^^^ 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 awaiting before this method call + | +LL | struct_().await.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 +error[E0308]: mismatched types + --> $DIR/issue-61076.rs:69:9 + | +LL | async fn tuple() -> Tuple { + | ----- the `Output` of this `async fn`'s expected opaque type +... +LL | Tuple(_) => {} + | ^^^^^^^^ expected opaque type, found struct `Tuple` + | + = note: expected opaque type `impl std::future::Future` + found struct `Tuple` +help: consider awaiting on the future + | +LL | match tuple().await { + | ^^^^^^ + +error: aborting due to 6 previous errors -Some errors have detailed explanations: E0277, E0599, E0609. +Some errors have detailed explanations: E0277, E0308, E0599, E0609. For more information about an error, try `rustc --explain E0277`. From 7cfcefd1fbbbfefbdc88feb7359e6364d7c0bf8a Mon Sep 17 00:00:00 2001 From: csmoe Date: Thu, 27 Aug 2020 12:09:34 +0800 Subject: [PATCH 5/5] add projection_ty_from_predicates query --- .../infer/error_reporting/mod.rs | 12 +--------- src/librustc_middle/query/mod.rs | 4 ++++ src/librustc_typeck/check/expr.rs | 10 +------- src/librustc_typeck/check/method/suggest.rs | 14 ++--------- src/librustc_typeck/collect.rs | 23 +++++++++++++++++++ 5 files changed, 31 insertions(+), 32 deletions(-) diff --git a/src/librustc_infer/infer/error_reporting/mod.rs b/src/librustc_infer/infer/error_reporting/mod.rs index 35b2d7e846839..d72ed72e3a8ac 100644 --- a/src/librustc_infer/infer/error_reporting/mod.rs +++ b/src/librustc_infer/infer/error_reporting/mod.rs @@ -1574,17 +1574,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { .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 projection_ty = self.tcx.projection_ty_from_predicates((def_id, item_def_id)); if let Some(projection_ty) = projection_ty { let projection_query = self.canonicalize_query( &ParamEnvAnd { param_env: self.tcx.param_env(def_id), value: projection_ty }, diff --git a/src/librustc_middle/query/mod.rs b/src/librustc_middle/query/mod.rs index d6836d2ee3665..e05752f08f631 100644 --- a/src/librustc_middle/query/mod.rs +++ b/src/librustc_middle/query/mod.rs @@ -173,6 +173,10 @@ rustc_queries! { desc { |tcx| "finding projection predicates for `{}`", tcx.def_path_str(key) } } + query projection_ty_from_predicates(key: (DefId, DefId)) -> Option> { + desc { |tcx| "finding projection type inside predicates of `{}`", tcx.def_path_str(key.0) } + } + query native_libraries(_: CrateNum) -> Lrc> { desc { "looking up the native libraries of a linked crate" } } diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs index 702dc30957ccf..82ed8fda8b489 100644 --- a/src/librustc_typeck/check/expr.rs +++ b/src/librustc_typeck/check/expr.rs @@ -1523,15 +1523,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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 projection_ty = self.tcx.projection_ty_from_predicates((def_id, item_def_id)); debug!("suggest_await_on_field_access: projection_ty={:?}", projection_ty); let cause = self.misc(expr.span); diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs index 3dd4c7c143967..5cae66bc5dac6 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -870,7 +870,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { call: &hir::Expr<'_>, span: Span, ) { - if let ty::Opaque(def_id, _substs) = ty.kind { + if let ty::Opaque(def_id, _) = ty.kind { let future_trait = self.tcx.require_lang_item(LangItem::Future, None); // Future::Output let item_def_id = self @@ -881,17 +881,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .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 projection_ty = self.tcx.projection_ty_from_predicates((def_id, item_def_id)); let cause = self.misc(span); let mut selcx = SelectionContext::new(&self.infcx); let mut obligations = vec![]; diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 1b472810ccf1d..7a3f7ec56a2ce 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -70,6 +70,7 @@ pub fn provide(providers: &mut Providers) { generics_of, predicates_of, predicates_defined_on, + projection_ty_from_predicates, explicit_predicates_of, super_predicates_of, type_param_predicates, @@ -2051,6 +2052,28 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat result } +fn projection_ty_from_predicates( + tcx: TyCtxt<'tcx>, + key: ( + // ty_def_id + DefId, + // def_id of `N` in `::N` + DefId, + ), +) -> Option> { + let (ty_def_id, item_def_id) = key; + let mut projection_ty = None; + for (predicate, _) in tcx.predicates_of(ty_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; + } + } + } + projection_ty +} + fn trait_associated_item_predicates( tcx: TyCtxt<'tcx>, def_id: DefId,