Rollup merge of #78297 - estebank:match-semicolon-2, r=oli-obk
Suggest calling await on method call and field access When encountering a failing method or field resolution on a `Future`, look at the `Output` and try the same operation on it. If successful, suggest calling `.await` on the `Future`. This had already been introduced in #72784, but at some point they stopped working. Built on top of #78214, only last commit is relevant. r? @oli-obk
This commit is contained in:
commit
86a4a38177
5 changed files with 90 additions and 95 deletions
|
@ -1669,7 +1669,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
||||||
self.note_error_origin(diag, cause, exp_found);
|
self.note_error_origin(diag, cause, exp_found);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_impl_future_output_ty(&self, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
|
pub fn get_impl_future_output_ty(&self, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
|
||||||
if let ty::Opaque(def_id, substs) = ty.kind() {
|
if let ty::Opaque(def_id, substs) = ty.kind() {
|
||||||
let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
|
let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
|
||||||
// Future::Output
|
// Future::Output
|
||||||
|
|
|
@ -42,7 +42,7 @@ use rustc_middle::ty::{AdtKind, Visibility};
|
||||||
use rustc_span::hygiene::DesugaringKind;
|
use rustc_span::hygiene::DesugaringKind;
|
||||||
use rustc_span::source_map::Span;
|
use rustc_span::source_map::Span;
|
||||||
use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
||||||
use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext};
|
use rustc_trait_selection::traits::{self, ObligationCauseCode};
|
||||||
|
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
@ -1583,51 +1583,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
err: &mut DiagnosticBuilder<'_>,
|
err: &mut DiagnosticBuilder<'_>,
|
||||||
field_ident: Ident,
|
field_ident: Ident,
|
||||||
base: &'tcx hir::Expr<'tcx>,
|
base: &'tcx hir::Expr<'tcx>,
|
||||||
expr: &'tcx hir::Expr<'tcx>,
|
ty: Ty<'tcx>,
|
||||||
def_id: DefId,
|
|
||||||
) {
|
) {
|
||||||
let param_env = self.tcx().param_env(def_id);
|
let output_ty = match self.infcx.get_impl_future_output_ty(ty) {
|
||||||
let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
|
Some(output_ty) => self.resolve_vars_if_possible(&output_ty),
|
||||||
// Future::Output
|
_ => return,
|
||||||
let item_def_id =
|
};
|
||||||
self.tcx.associated_items(future_trait).in_definition_order().next().unwrap().def_id;
|
let mut add_label = true;
|
||||||
|
if let ty::Adt(def, _) = output_ty.kind() {
|
||||||
let projection_ty = self.tcx.projection_ty_from_predicates((def_id, item_def_id));
|
// no field access on enum type
|
||||||
debug!("suggest_await_on_field_access: projection_ty={:?}", projection_ty);
|
if !def.is_enum() {
|
||||||
|
if def.non_enum_variant().fields.iter().any(|field| field.ident == field_ident) {
|
||||||
let cause = self.misc(expr.span);
|
add_label = false;
|
||||||
let mut selcx = SelectionContext::new(&self.infcx);
|
err.span_label(
|
||||||
|
field_ident.span,
|
||||||
let mut obligations = vec![];
|
"field not available in `impl Future`, but it is available in its `Output`",
|
||||||
if let Some(projection_ty) = projection_ty {
|
);
|
||||||
let normalized_ty = rustc_trait_selection::traits::normalize_projection_type(
|
err.span_suggestion_verbose(
|
||||||
&mut selcx,
|
base.span.shrink_to_hi(),
|
||||||
param_env,
|
"consider `await`ing on the `Future` and access the field of its `Output`",
|
||||||
projection_ty,
|
".await".to_string(),
|
||||||
cause,
|
Applicability::MaybeIncorrect,
|
||||||
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() {
|
|
||||||
// no field access on enum type
|
|
||||||
if !def.is_enum() {
|
|
||||||
if def.non_enum_variant().fields.iter().any(|field| field.ident == field_ident)
|
|
||||||
{
|
|
||||||
err.span_suggestion_verbose(
|
|
||||||
base.span.shrink_to_hi(),
|
|
||||||
"consider awaiting before field access",
|
|
||||||
".await".to_string(),
|
|
||||||
Applicability::MaybeIncorrect,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if add_label {
|
||||||
|
err.span_label(field_ident.span, &format!("field not found in `{}`", ty));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ban_nonexisting_field(
|
fn ban_nonexisting_field(
|
||||||
|
@ -1656,8 +1639,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
ty::Param(param_ty) => {
|
ty::Param(param_ty) => {
|
||||||
self.point_at_param_definition(&mut err, param_ty);
|
self.point_at_param_definition(&mut err, param_ty);
|
||||||
}
|
}
|
||||||
ty::Opaque(def_id, _) => {
|
ty::Opaque(_, _) => {
|
||||||
self.suggest_await_on_field_access(&mut err, field, base, expr, def_id);
|
self.suggest_await_on_field_access(&mut err, field, base, expr_t.peel_refs());
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,6 @@ use rustc_span::symbol::{kw, sym, Ident};
|
||||||
use rustc_span::{source_map, FileName, Span};
|
use rustc_span::{source_map, FileName, Span};
|
||||||
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
|
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
|
||||||
use rustc_trait_selection::traits::Obligation;
|
use rustc_trait_selection::traits::Obligation;
|
||||||
use rustc_trait_selection::traits::SelectionContext;
|
|
||||||
|
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
|
@ -870,46 +869,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
call: &hir::Expr<'_>,
|
call: &hir::Expr<'_>,
|
||||||
span: Span,
|
span: Span,
|
||||||
) {
|
) {
|
||||||
if let ty::Opaque(def_id, _) = *ty.kind() {
|
let output_ty = match self.infcx.get_impl_future_output_ty(ty) {
|
||||||
let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
|
Some(output_ty) => self.resolve_vars_if_possible(&output_ty),
|
||||||
// Future::Output
|
_ => return,
|
||||||
let item_def_id = self
|
};
|
||||||
.tcx
|
let method_exists = self.method_exists(item_name, output_ty, call.hir_id, true);
|
||||||
.associated_items(future_trait)
|
debug!("suggest_await_before_method: is_method_exist={}", method_exists);
|
||||||
.in_definition_order()
|
if method_exists {
|
||||||
.next()
|
err.span_suggestion_verbose(
|
||||||
.unwrap()
|
span.shrink_to_lo(),
|
||||||
.def_id;
|
"consider `await`ing on the `Future` and calling the method on its `Output`",
|
||||||
|
"await.".to_string(),
|
||||||
let projection_ty = self.tcx.projection_ty_from_predicates((def_id, item_def_id));
|
Applicability::MaybeIncorrect,
|
||||||
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 method_exists {
|
|
||||||
err.span_suggestion_verbose(
|
|
||||||
span.shrink_to_lo(),
|
|
||||||
"consider awaiting before this method call",
|
|
||||||
"await.".to_string(),
|
|
||||||
Applicability::MaybeIncorrect,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,14 @@ async fn foo() -> Result<(), ()> {
|
||||||
|
|
||||||
async fn bar() -> Result<(), ()> {
|
async fn bar() -> Result<(), ()> {
|
||||||
foo()?; //~ ERROR the `?` operator can only be applied to values that implement `Try`
|
foo()?; //~ ERROR the `?` operator can only be applied to values that implement `Try`
|
||||||
|
//~^ NOTE the `?` operator cannot be applied to type `impl Future`
|
||||||
|
//~| HELP the trait `Try` is not implemented for `impl Future`
|
||||||
|
//~| NOTE required by `into_result`
|
||||||
|
//~| HELP consider `await`ing on the `Future`
|
||||||
|
//~| NOTE in this expansion of desugaring of operator `?`
|
||||||
|
//~| NOTE in this expansion of desugaring of operator `?`
|
||||||
|
//~| NOTE in this expansion of desugaring of operator `?`
|
||||||
|
//~| NOTE in this expansion of desugaring of operator `?`
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,25 +56,42 @@ async fn struct_() -> Struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn tuple() -> Tuple {
|
async fn tuple() -> Tuple {
|
||||||
|
//~^ NOTE the `Output` of this `async fn`'s expected opaque type
|
||||||
Tuple(1i32)
|
Tuple(1i32)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn baz() -> Result<(), ()> {
|
async fn baz() -> Result<(), ()> {
|
||||||
let t = T;
|
let t = T;
|
||||||
t?; //~ ERROR the `?` operator can only be applied to values that implement `Try`
|
t?; //~ ERROR the `?` operator can only be applied to values that implement `Try`
|
||||||
|
//~^ NOTE the `?` operator cannot be applied to type `T`
|
||||||
|
//~| HELP the trait `Try` is not implemented for `T`
|
||||||
|
//~| NOTE required by `into_result`
|
||||||
|
//~| HELP consider `await`ing on the `Future`
|
||||||
|
//~| NOTE in this expansion of desugaring of operator `?`
|
||||||
|
//~| NOTE in this expansion of desugaring of operator `?`
|
||||||
|
//~| NOTE in this expansion of desugaring of operator `?`
|
||||||
|
//~| NOTE in this expansion of desugaring of operator `?`
|
||||||
|
|
||||||
|
|
||||||
let _: i32 = tuple().0; //~ ERROR no field `0`
|
let _: i32 = tuple().0; //~ ERROR no field `0`
|
||||||
|
//~^ HELP consider `await`ing on the `Future`
|
||||||
|
//~| NOTE field not available in `impl Future`
|
||||||
|
|
||||||
let _: i32 = struct_().a; //~ ERROR no field `a`
|
let _: i32 = struct_().a; //~ ERROR no field `a`
|
||||||
|
//~^ HELP consider `await`ing on the `Future`
|
||||||
|
//~| NOTE field not available in `impl Future`
|
||||||
|
|
||||||
struct_().method(); //~ ERROR no method named
|
struct_().method(); //~ ERROR no method named
|
||||||
|
//~^ NOTE method not found in `impl Future`
|
||||||
|
//~| HELP consider `await`ing on the `Future`
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn match_() {
|
async fn match_() {
|
||||||
match tuple() {
|
match tuple() { //~ HELP consider `await`ing on the `Future`
|
||||||
Tuple(_) => {} //~ ERROR mismatched types
|
Tuple(_) => {} //~ ERROR mismatched types
|
||||||
|
//~^ NOTE expected opaque type, found struct `Tuple`
|
||||||
|
//~| NOTE expected opaque type `impl Future`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ LL | foo().await?;
|
||||||
| ^^^^^^
|
| ^^^^^^
|
||||||
|
|
||||||
error[E0277]: the `?` operator can only be applied to values that implement `Try`
|
error[E0277]: the `?` operator can only be applied to values that implement `Try`
|
||||||
--> $DIR/issue-61076.rs:56:5
|
--> $DIR/issue-61076.rs:65:5
|
||||||
|
|
|
|
||||||
LL | t?;
|
LL | t?;
|
||||||
| ^^ the `?` operator cannot be applied to type `T`
|
| ^^ the `?` operator cannot be applied to type `T`
|
||||||
|
@ -25,25 +25,40 @@ LL | t.await?;
|
||||||
| ^^^^^^
|
| ^^^^^^
|
||||||
|
|
||||||
error[E0609]: no field `0` on type `impl Future`
|
error[E0609]: no field `0` on type `impl Future`
|
||||||
--> $DIR/issue-61076.rs:58:26
|
--> $DIR/issue-61076.rs:76:26
|
||||||
|
|
|
|
||||||
LL | let _: i32 = tuple().0;
|
LL | let _: i32 = tuple().0;
|
||||||
| ^
|
| ^ field not available in `impl Future`, but it is available in its `Output`
|
||||||
|
|
|
||||||
|
help: consider `await`ing on the `Future` and access the field of its `Output`
|
||||||
|
|
|
||||||
|
LL | let _: i32 = tuple().await.0;
|
||||||
|
| ^^^^^^
|
||||||
|
|
||||||
error[E0609]: no field `a` on type `impl Future`
|
error[E0609]: no field `a` on type `impl Future`
|
||||||
--> $DIR/issue-61076.rs:60:28
|
--> $DIR/issue-61076.rs:80:28
|
||||||
|
|
|
|
||||||
LL | let _: i32 = struct_().a;
|
LL | let _: i32 = struct_().a;
|
||||||
| ^
|
| ^ field not available in `impl Future`, but it is available in its `Output`
|
||||||
|
|
|
||||||
|
help: consider `await`ing on the `Future` and access the field of its `Output`
|
||||||
|
|
|
||||||
|
LL | let _: i32 = struct_().await.a;
|
||||||
|
| ^^^^^^
|
||||||
|
|
||||||
error[E0599]: no method named `method` found for opaque type `impl Future` in the current scope
|
error[E0599]: no method named `method` found for opaque type `impl Future` in the current scope
|
||||||
--> $DIR/issue-61076.rs:62:15
|
--> $DIR/issue-61076.rs:84:15
|
||||||
|
|
|
|
||||||
LL | struct_().method();
|
LL | struct_().method();
|
||||||
| ^^^^^^ method not found in `impl Future`
|
| ^^^^^^ method not found in `impl Future`
|
||||||
|
|
|
||||||
|
help: consider `await`ing on the `Future` and calling the method on its `Output`
|
||||||
|
|
|
||||||
|
LL | struct_().await.method();
|
||||||
|
| ^^^^^^
|
||||||
|
|
||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
--> $DIR/issue-61076.rs:69:9
|
--> $DIR/issue-61076.rs:92:9
|
||||||
|
|
|
|
||||||
LL | async fn tuple() -> Tuple {
|
LL | async fn tuple() -> Tuple {
|
||||||
| ----- the `Output` of this `async fn`'s expected opaque type
|
| ----- the `Output` of this `async fn`'s expected opaque type
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue