Skip to content

Commit 1c8490e

Browse files
committed
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.
1 parent f5d7443 commit 1c8490e

File tree

5 files changed

+90
-95
lines changed

5 files changed

+90
-95
lines changed

compiler/rustc_infer/src/infer/error_reporting/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1669,7 +1669,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
16691669
self.note_error_origin(diag, cause, exp_found);
16701670
}
16711671

1672-
fn get_impl_future_output_ty(&self, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
1672+
pub fn get_impl_future_output_ty(&self, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
16731673
if let ty::Opaque(def_id, substs) = ty.kind() {
16741674
let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
16751675
// Future::Output

compiler/rustc_typeck/src/check/expr.rs

+27-44
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ use rustc_middle::ty::{AdtKind, Visibility};
4242
use rustc_span::hygiene::DesugaringKind;
4343
use rustc_span::source_map::Span;
4444
use rustc_span::symbol::{kw, sym, Ident, Symbol};
45-
use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext};
45+
use rustc_trait_selection::traits::{self, ObligationCauseCode};
4646

4747
use std::fmt::Display;
4848

@@ -1574,51 +1574,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
15741574
err: &mut DiagnosticBuilder<'_>,
15751575
field_ident: Ident,
15761576
base: &'tcx hir::Expr<'tcx>,
1577-
expr: &'tcx hir::Expr<'tcx>,
1578-
def_id: DefId,
1577+
ty: Ty<'tcx>,
15791578
) {
1580-
let param_env = self.tcx().param_env(def_id);
1581-
let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
1582-
// Future::Output
1583-
let item_def_id =
1584-
self.tcx.associated_items(future_trait).in_definition_order().next().unwrap().def_id;
1585-
1586-
let projection_ty = self.tcx.projection_ty_from_predicates((def_id, item_def_id));
1587-
debug!("suggest_await_on_field_access: projection_ty={:?}", projection_ty);
1588-
1589-
let cause = self.misc(expr.span);
1590-
let mut selcx = SelectionContext::new(&self.infcx);
1591-
1592-
let mut obligations = vec![];
1593-
if let Some(projection_ty) = projection_ty {
1594-
let normalized_ty = rustc_trait_selection::traits::normalize_projection_type(
1595-
&mut selcx,
1596-
param_env,
1597-
projection_ty,
1598-
cause,
1599-
0,
1600-
&mut obligations,
1601-
);
1602-
debug!(
1603-
"suggest_await_on_field_access: normalized_ty={:?}, ty_kind={:?}",
1604-
self.resolve_vars_if_possible(&normalized_ty),
1605-
normalized_ty.kind(),
1606-
);
1607-
if let ty::Adt(def, _) = normalized_ty.kind() {
1608-
// no field access on enum type
1609-
if !def.is_enum() {
1610-
if def.non_enum_variant().fields.iter().any(|field| field.ident == field_ident)
1611-
{
1612-
err.span_suggestion_verbose(
1613-
base.span.shrink_to_hi(),
1614-
"consider awaiting before field access",
1615-
".await".to_string(),
1616-
Applicability::MaybeIncorrect,
1617-
);
1618-
}
1579+
let output_ty = match self.infcx.get_impl_future_output_ty(ty) {
1580+
Some(output_ty) => self.resolve_vars_if_possible(&output_ty),
1581+
_ => return,
1582+
};
1583+
let mut add_label = true;
1584+
if let ty::Adt(def, _) = output_ty.kind() {
1585+
// no field access on enum type
1586+
if !def.is_enum() {
1587+
if def.non_enum_variant().fields.iter().any(|field| field.ident == field_ident) {
1588+
add_label = false;
1589+
err.span_label(
1590+
field_ident.span,
1591+
"field not available in `impl Future`, but it is available in its `Output`",
1592+
);
1593+
err.span_suggestion_verbose(
1594+
base.span.shrink_to_hi(),
1595+
"consider `await`ing on the `Future` and access the field of its `Output`",
1596+
".await".to_string(),
1597+
Applicability::MaybeIncorrect,
1598+
);
16191599
}
16201600
}
16211601
}
1602+
if add_label {
1603+
err.span_label(field_ident.span, &format!("field not found in `{}`", ty));
1604+
}
16221605
}
16231606

16241607
fn ban_nonexisting_field(
@@ -1647,8 +1630,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
16471630
ty::Param(param_ty) => {
16481631
self.point_at_param_definition(&mut err, param_ty);
16491632
}
1650-
ty::Opaque(def_id, _) => {
1651-
self.suggest_await_on_field_access(&mut err, field, base, expr, def_id);
1633+
ty::Opaque(_, _) => {
1634+
self.suggest_await_on_field_access(&mut err, field, base, expr_t.peel_refs());
16521635
}
16531636
_ => {}
16541637
}

compiler/rustc_typeck/src/check/method/suggest.rs

+13-41
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ use rustc_span::symbol::{kw, sym, Ident};
2121
use rustc_span::{source_map, FileName, Span};
2222
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
2323
use rustc_trait_selection::traits::Obligation;
24-
use rustc_trait_selection::traits::SelectionContext;
2524

2625
use std::cmp::Ordering;
2726

@@ -870,46 +869,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
870869
call: &hir::Expr<'_>,
871870
span: Span,
872871
) {
873-
if let ty::Opaque(def_id, _) = *ty.kind() {
874-
let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
875-
// Future::Output
876-
let item_def_id = self
877-
.tcx
878-
.associated_items(future_trait)
879-
.in_definition_order()
880-
.next()
881-
.unwrap()
882-
.def_id;
883-
884-
let projection_ty = self.tcx.projection_ty_from_predicates((def_id, item_def_id));
885-
let cause = self.misc(span);
886-
let mut selcx = SelectionContext::new(&self.infcx);
887-
let mut obligations = vec![];
888-
if let Some(projection_ty) = projection_ty {
889-
let normalized_ty = rustc_trait_selection::traits::normalize_projection_type(
890-
&mut selcx,
891-
self.param_env,
892-
projection_ty,
893-
cause,
894-
0,
895-
&mut obligations,
896-
);
897-
debug!(
898-
"suggest_await_before_method: normalized_ty={:?}, ty_kind={:?}",
899-
self.resolve_vars_if_possible(&normalized_ty),
900-
normalized_ty.kind(),
901-
);
902-
let method_exists = self.method_exists(item_name, normalized_ty, call.hir_id, true);
903-
debug!("suggest_await_before_method: is_method_exist={}", method_exists);
904-
if method_exists {
905-
err.span_suggestion_verbose(
906-
span.shrink_to_lo(),
907-
"consider awaiting before this method call",
908-
"await.".to_string(),
909-
Applicability::MaybeIncorrect,
910-
);
911-
}
912-
}
872+
let output_ty = match self.infcx.get_impl_future_output_ty(ty) {
873+
Some(output_ty) => self.resolve_vars_if_possible(&output_ty),
874+
_ => return,
875+
};
876+
let method_exists = self.method_exists(item_name, output_ty, call.hir_id, true);
877+
debug!("suggest_await_before_method: is_method_exist={}", method_exists);
878+
if method_exists {
879+
err.span_suggestion_verbose(
880+
span.shrink_to_lo(),
881+
"consider `await`ing on the `Future` and calling the method on its `Output`",
882+
"await.".to_string(),
883+
Applicability::MaybeIncorrect,
884+
);
913885
}
914886
}
915887

src/test/ui/async-await/issue-61076.rs

+27-2
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,14 @@ async fn foo() -> Result<(), ()> {
4040

4141
async fn bar() -> Result<(), ()> {
4242
foo()?; //~ ERROR the `?` operator can only be applied to values that implement `Try`
43+
//~^ NOTE the `?` operator cannot be applied to type `impl Future`
44+
//~| HELP the trait `Try` is not implemented for `impl Future`
45+
//~| NOTE required by `into_result`
46+
//~| HELP consider `await`ing on the `Future`
47+
//~| NOTE in this expansion of desugaring of operator `?`
48+
//~| NOTE in this expansion of desugaring of operator `?`
49+
//~| NOTE in this expansion of desugaring of operator `?`
50+
//~| NOTE in this expansion of desugaring of operator `?`
4351
Ok(())
4452
}
4553

@@ -48,25 +56,42 @@ async fn struct_() -> Struct {
4856
}
4957

5058
async fn tuple() -> Tuple {
59+
//~^ NOTE the `Output` of this `async fn`'s expected opaque type
5160
Tuple(1i32)
5261
}
5362

5463
async fn baz() -> Result<(), ()> {
5564
let t = T;
5665
t?; //~ ERROR the `?` operator can only be applied to values that implement `Try`
66+
//~^ NOTE the `?` operator cannot be applied to type `T`
67+
//~| HELP the trait `Try` is not implemented for `T`
68+
//~| NOTE required by `into_result`
69+
//~| HELP consider `await`ing on the `Future`
70+
//~| NOTE in this expansion of desugaring of operator `?`
71+
//~| NOTE in this expansion of desugaring of operator `?`
72+
//~| NOTE in this expansion of desugaring of operator `?`
73+
//~| NOTE in this expansion of desugaring of operator `?`
74+
5775

5876
let _: i32 = tuple().0; //~ ERROR no field `0`
77+
//~^ HELP consider `await`ing on the `Future`
78+
//~| NOTE field not available in `impl Future`
5979

6080
let _: i32 = struct_().a; //~ ERROR no field `a`
81+
//~^ HELP consider `await`ing on the `Future`
82+
//~| NOTE field not available in `impl Future`
6183

6284
struct_().method(); //~ ERROR no method named
63-
85+
//~^ NOTE method not found in `impl Future`
86+
//~| HELP consider `await`ing on the `Future`
6487
Ok(())
6588
}
6689

6790
async fn match_() {
68-
match tuple() {
91+
match tuple() { //~ HELP consider `await`ing on the `Future`
6992
Tuple(_) => {} //~ ERROR mismatched types
93+
//~^ NOTE expected opaque type, found struct `Tuple`
94+
//~| NOTE expected opaque type `impl Future`
7095
}
7196
}
7297

src/test/ui/async-await/issue-61076.stderr

+22-7
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ LL | foo().await?;
1212
| ^^^^^^
1313

1414
error[E0277]: the `?` operator can only be applied to values that implement `Try`
15-
--> $DIR/issue-61076.rs:56:5
15+
--> $DIR/issue-61076.rs:65:5
1616
|
1717
LL | t?;
1818
| ^^ the `?` operator cannot be applied to type `T`
@@ -25,25 +25,40 @@ LL | t.await?;
2525
| ^^^^^^
2626

2727
error[E0609]: no field `0` on type `impl Future`
28-
--> $DIR/issue-61076.rs:58:26
28+
--> $DIR/issue-61076.rs:76:26
2929
|
3030
LL | let _: i32 = tuple().0;
31-
| ^
31+
| ^ field not available in `impl Future`, but it is available in its `Output`
32+
|
33+
help: consider `await`ing on the `Future` and access the field of its `Output`
34+
|
35+
LL | let _: i32 = tuple().await.0;
36+
| ^^^^^^
3237

3338
error[E0609]: no field `a` on type `impl Future`
34-
--> $DIR/issue-61076.rs:60:28
39+
--> $DIR/issue-61076.rs:80:28
3540
|
3641
LL | let _: i32 = struct_().a;
37-
| ^
42+
| ^ field not available in `impl Future`, but it is available in its `Output`
43+
|
44+
help: consider `await`ing on the `Future` and access the field of its `Output`
45+
|
46+
LL | let _: i32 = struct_().await.a;
47+
| ^^^^^^
3848

3949
error[E0599]: no method named `method` found for opaque type `impl Future` in the current scope
40-
--> $DIR/issue-61076.rs:62:15
50+
--> $DIR/issue-61076.rs:84:15
4151
|
4252
LL | struct_().method();
4353
| ^^^^^^ method not found in `impl Future`
54+
|
55+
help: consider `await`ing on the `Future` and calling the method on its `Output`
56+
|
57+
LL | struct_().await.method();
58+
| ^^^^^^
4459

4560
error[E0308]: mismatched types
46-
--> $DIR/issue-61076.rs:69:9
61+
--> $DIR/issue-61076.rs:92:9
4762
|
4863
LL | async fn tuple() -> Tuple {
4964
| ----- the `Output` of this `async fn`'s expected opaque type

0 commit comments

Comments
 (0)