suggest await on field access

This commit is contained in:
csmoe 2020-08-16 20:25:22 +08:00
parent 1d30de6202
commit 1de0dd9531
3 changed files with 106 additions and 41 deletions

View File

@ -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);
}
_ => {}
}

View File

@ -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 = 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(())
}

View File

@ -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<T>(gen: T) -> impl Future<Output = T::Return>
| ------------------------------- 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`.