From 1de0dd9531bfae1db458c0d88830a5c09203a100 Mon Sep 17 00:00:00 2001 From: csmoe Date: Sun, 16 Aug 2020 20:25:22 +0800 Subject: [PATCH] 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 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 { 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 = tuple().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 + 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 + --> $DIR/issue-61076.rs:56:28 | -LL | }.a; - | ^ +LL | let _: i32 = struct_().a; + | ----------^ + | | + | help: consider await before field access: `struct_().await.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 4 previous errors -error: aborting due to 5 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`.