Skip to content

Commit 1de0dd9

Browse files
committed
suggest await on field access
1 parent 1d30de6 commit 1de0dd9

File tree

3 files changed

+106
-41
lines changed

3 files changed

+106
-41
lines changed

src/librustc_typeck/check/expr.rs

+73-1
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,14 @@ use rustc_infer::infer;
3131
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
3232
use rustc_middle::ty;
3333
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase};
34+
use rustc_middle::ty::subst::SubstsRef;
3435
use rustc_middle::ty::Ty;
3536
use rustc_middle::ty::TypeFoldable;
3637
use rustc_middle::ty::{AdtKind, Visibility};
3738
use rustc_span::hygiene::DesugaringKind;
3839
use rustc_span::source_map::Span;
3940
use rustc_span::symbol::{kw, sym, Ident, Symbol};
40-
use rustc_trait_selection::traits::{self, ObligationCauseCode};
41+
use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext};
4142

4243
use std::fmt::Display;
4344

@@ -1509,13 +1510,81 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
15091510
self.tcx().ty_error()
15101511
}
15111512

1513+
fn suggest_await_on_field_access(
1514+
&self,
1515+
err: &mut DiagnosticBuilder<'_>,
1516+
field_ident: Ident,
1517+
base: &'tcx hir::Expr<'tcx>,
1518+
expr: &'tcx hir::Expr<'tcx>,
1519+
def_id: DefId,
1520+
substs: SubstsRef<'tcx>,
1521+
) {
1522+
let param_env = self.tcx().param_env(def_id);
1523+
let future_trait = self.tcx.require_lang_item(lang_items::FutureTraitLangItem, None);
1524+
let future_trait_ref = ty::TraitRef { def_id: future_trait, substs };
1525+
// Future::Output
1526+
let future_projection = ty::ProjectionTy::from_ref_and_name(
1527+
self.tcx,
1528+
future_trait_ref,
1529+
Ident::with_dummy_span(sym::Output),
1530+
);
1531+
1532+
let mut projection_ty = None;
1533+
for (predicate, _) in self.tcx.predicates_of(def_id).predicates {
1534+
if let ty::PredicateAtom::Projection(projection_predicate) = predicate.skip_binders() {
1535+
if future_projection.item_def_id == projection_predicate.projection_ty.item_def_id {
1536+
projection_ty = Some(projection_predicate.projection_ty);
1537+
break;
1538+
}
1539+
}
1540+
}
1541+
debug!("suggest_await_on_field_access: projection_ty={:?}", projection_ty);
1542+
1543+
let cause = self.misc(expr.span);
1544+
let mut selcx = SelectionContext::new(&self.infcx);
1545+
1546+
let mut obligations = vec![];
1547+
if let Some(projection_ty) = projection_ty {
1548+
let normalized_ty = rustc_trait_selection::traits::normalize_projection_type(
1549+
&mut selcx,
1550+
param_env,
1551+
projection_ty,
1552+
cause,
1553+
0,
1554+
&mut obligations,
1555+
);
1556+
debug!(
1557+
"suggest_await_on_field_access: normalized_ty={:?}, ty_kind={:?}",
1558+
self.resolve_vars_if_possible(&normalized_ty),
1559+
normalized_ty.kind,
1560+
);
1561+
if let ty::Adt(def, _) = normalized_ty.kind {
1562+
if def.non_enum_variant().fields.iter().any(|field| field.ident == field_ident) {
1563+
if let Ok(base) = self.tcx.sess.source_map().span_to_snippet(base.span) {
1564+
let suggestion = format!("{}.await.{}", base, field_ident);
1565+
err.span_suggestion(
1566+
expr.span,
1567+
"consider await before field access",
1568+
suggestion,
1569+
Applicability::MaybeIncorrect,
1570+
);
1571+
}
1572+
}
1573+
}
1574+
}
1575+
}
1576+
15121577
fn ban_nonexisting_field(
15131578
&self,
15141579
field: Ident,
15151580
base: &'tcx hir::Expr<'tcx>,
15161581
expr: &'tcx hir::Expr<'tcx>,
15171582
expr_t: Ty<'tcx>,
15181583
) {
1584+
debug!(
1585+
"ban_nonexisting_field: field={:?}, base={:?}, expr={:?}, expr_ty={:?}",
1586+
field, base, expr, expr_t
1587+
);
15191588
let mut err = self.no_such_field_err(field.span, field, expr_t);
15201589

15211590
match expr_t.peel_refs().kind {
@@ -1531,6 +1600,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
15311600
ty::Param(param_ty) => {
15321601
self.point_at_param_definition(&mut err, param_ty);
15331602
}
1603+
ty::Opaque(def_id, subts) => {
1604+
self.suggest_await_on_field_access(&mut err, field, base, expr, def_id, subts);
1605+
}
15341606
_ => {}
15351607
}
15361608

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

+19-11
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,20 @@ use core::task::{Context, Poll};
66

77
struct T;
88

9-
struct UnionStruct(i32);
9+
struct Tuple(i32);
1010

1111
struct Struct {
1212
a: i32
1313
}
1414

15-
enum Enum {
16-
A
15+
impl Future for Struct {
16+
type Output = Struct;
17+
fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> { Poll::Pending }
18+
}
19+
20+
impl Future for Tuple {
21+
type Output = Tuple;
22+
fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> { Poll::Pending }
1723
}
1824

1925
impl Future for T {
@@ -33,19 +39,21 @@ async fn bar() -> Result<(), ()> {
3339
Ok(())
3440
}
3541

42+
async fn struct_() -> Struct {
43+
Struct { a: 1 }
44+
}
45+
46+
async fn tuple() -> Tuple {
47+
Tuple(1i32)
48+
}
49+
3650
async fn baz() -> Result<(), ()> {
3751
let t = T;
3852
t?; //~ ERROR the `?` operator can only be applied to values that implement `std::ops::Try`
3953

40-
let _: i32 = async {
41-
UnionStruct(1i32)
42-
}.0; //~ ERROR no field `0`
43-
44-
let _: i32 = async {
45-
Struct { a: 1i32 }
46-
}.a; //~ ERROR no field `a`
54+
let _: i32 = tuple().0; //~ ERROR no field `0`
4755

48-
if let Enum::A = async { Enum::A } {} //~ ERROR mismatched type
56+
let _: i32 = struct_().a; //~ ERROR no field `a`
4957

5058
Ok(())
5159
}
+14-29
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try`
2-
--> $DIR/issue-61076.rs:32:5
2+
--> $DIR/issue-61076.rs:38:5
33
|
44
LL | foo()?;
55
| ^^^^^^
@@ -11,7 +11,7 @@ LL | foo()?;
1111
= note: required by `std::ops::Try::into_result`
1212

1313
error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try`
14-
--> $DIR/issue-61076.rs:38:5
14+
--> $DIR/issue-61076.rs:52:5
1515
|
1616
LL | t?;
1717
| ^^
@@ -23,37 +23,22 @@ LL | t?;
2323
= note: required by `std::ops::Try::into_result`
2424

2525
error[E0609]: no field `0` on type `impl std::future::Future`
26-
--> $DIR/issue-61076.rs:42:7
26+
--> $DIR/issue-61076.rs:54:26
2727
|
28-
LL | }.0;
29-
| ^
28+
LL | let _: i32 = tuple().0;
29+
| --------^
30+
| |
31+
| help: consider await before field access: `tuple().await.0`
3032

3133
error[E0609]: no field `a` on type `impl std::future::Future`
32-
--> $DIR/issue-61076.rs:46:7
33-
|
34-
LL | }.a;
35-
| ^
36-
37-
error[E0308]: mismatched types
38-
--> $DIR/issue-61076.rs:48:12
39-
|
40-
LL | A
41-
| - unit variant defined here
42-
...
43-
LL | if let Enum::A = async { Enum::A } {}
44-
| ^^^^^^^ ----------- the expected generator
45-
| |
46-
| expected opaque type, found enum `Enum`
47-
|
48-
::: $SRC_DIR/libcore/future/mod.rs:LL:COL
49-
|
50-
LL | pub const fn from_generator<T>(gen: T) -> impl Future<Output = T::Return>
51-
| ------------------------------- the expected opaque type
34+
--> $DIR/issue-61076.rs:56:28
5235
|
53-
= note: expected opaque type `impl std::future::Future`
54-
found enum `Enum`
36+
LL | let _: i32 = struct_().a;
37+
| ----------^
38+
| |
39+
| help: consider await before field access: `struct_().await.a`
5540

56-
error: aborting due to 5 previous errors
41+
error: aborting due to 4 previous errors
5742

58-
Some errors have detailed explanations: E0277, E0308, E0609.
43+
Some errors have detailed explanations: E0277, E0609.
5944
For more information about an error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)