From 8ee206a80dab320ee76a2aa9324c587368df3921 Mon Sep 17 00:00:00 2001 From: csmoe Date: Thu, 20 Aug 2020 18:42:08 +0800 Subject: [PATCH] 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 8212958510a..35b2d7e8468 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::{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::{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 @@ enum Mismatch<'a> { }; 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 @@ enum Mismatch<'a> { 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 40088bc0690..afd4413069e 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -28,7 +28,7 @@ pub fn check_match( }; // 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 @@ pub fn check_match( // #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 0f2e4240c01..702dc30957c 100644 --- a/src/librustc_typeck/check/expr.rs +++ b/src/librustc_typeck/check/expr.rs @@ -1518,7 +1518,7 @@ fn suggest_await_on_field_access( 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 @@ fn suggest_await_on_field_access( ); 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 8451918568d..3dd4c7c1439 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -911,15 +911,13 @@ fn suggest_await_before_method( ); 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 743f1959828..e383a9126f7 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 692117a06b0..69b6e8c3cf5 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 { + | ^^^^^^ -Some errors have detailed explanations: E0277, E0599, E0609. +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0277, E0308, E0599, E0609. For more information about an error, try `rustc --explain E0277`.