Skip to content

Commit 8ee206a

Browse files
committed
suggest await on unexpected types
1 parent 2271b08 commit 8ee206a

File tree

6 files changed

+126
-32
lines changed

6 files changed

+126
-32
lines changed

src/librustc_infer/infer/error_reporting/mod.rs

+70
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ use super::region_constraints::GenericKind;
5050
use super::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TypeTrace, ValuePairs};
5151

5252
use crate::infer;
53+
use crate::infer::OriginalQueryValues;
5354
use crate::traits::error_reporting::report_object_safety_error;
5455
use crate::traits::{
5556
IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
@@ -60,8 +61,10 @@ use rustc_errors::{pluralize, struct_span_err};
6061
use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString};
6162
use rustc_hir as hir;
6263
use rustc_hir::def_id::DefId;
64+
use rustc_hir::lang_items::LangItem;
6365
use rustc_hir::{Item, ItemKind, Node};
6466
use rustc_middle::ty::error::TypeError;
67+
use rustc_middle::ty::ParamEnvAnd;
6568
use rustc_middle::ty::{
6669
self,
6770
subst::{Subst, SubstsRef},
@@ -1529,6 +1532,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
15291532
};
15301533
if let Some(exp_found) = exp_found {
15311534
self.suggest_as_ref_where_appropriate(span, &exp_found, diag);
1535+
self.suggest_await_on_expect_found(cause, span, &exp_found, diag);
15321536
}
15331537

15341538
// In some (most?) cases cause.body_id points to actual body, but in some cases
@@ -1547,6 +1551,72 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
15471551
self.note_error_origin(diag, cause, exp_found);
15481552
}
15491553

1554+
fn suggest_await_on_expect_found(
1555+
&self,
1556+
cause: &ObligationCause<'tcx>,
1557+
exp_span: Span,
1558+
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
1559+
diag: &mut DiagnosticBuilder<'tcx>,
1560+
) {
1561+
debug!(
1562+
"suggest_await_on_expect_found: exp_span={:?}, expected_ty={:?}, found_ty={:?}",
1563+
exp_span, exp_found.expected, exp_found.found
1564+
);
1565+
1566+
if let ty::Opaque(def_id, _) = exp_found.expected.kind {
1567+
let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
1568+
// Future::Output
1569+
let item_def_id = self
1570+
.tcx
1571+
.associated_items(future_trait)
1572+
.in_definition_order()
1573+
.next()
1574+
.unwrap()
1575+
.def_id;
1576+
1577+
let mut projection_ty = None;
1578+
for (predicate, _) in self.tcx.predicates_of(def_id).predicates {
1579+
if let ty::PredicateAtom::Projection(projection_predicate) =
1580+
predicate.skip_binders()
1581+
{
1582+
if item_def_id == projection_predicate.projection_ty.item_def_id {
1583+
projection_ty = Some(projection_predicate.projection_ty);
1584+
break;
1585+
}
1586+
}
1587+
}
1588+
if let Some(projection_ty) = projection_ty {
1589+
let projection_query = self.canonicalize_query(
1590+
&ParamEnvAnd { param_env: self.tcx.param_env(def_id), value: projection_ty },
1591+
&mut OriginalQueryValues::default(),
1592+
);
1593+
if let Ok(resp) = self.tcx.normalize_projection_ty(projection_query) {
1594+
let normalized_ty = resp.value.value.normalized_ty;
1595+
debug!("suggest_await_on_expect_found: normalized={:?}", normalized_ty);
1596+
if ty::TyS::same_type(normalized_ty, exp_found.found) {
1597+
let span = if let ObligationCauseCode::Pattern {
1598+
span,
1599+
origin_expr: _,
1600+
root_ty: _,
1601+
} = cause.code
1602+
{
1603+
// scrutinee's span
1604+
span.unwrap_or(exp_span)
1605+
} else {
1606+
exp_span
1607+
};
1608+
diag.span_suggestion_verbose(
1609+
span.shrink_to_hi(),
1610+
"consider awaiting on the future",
1611+
".await".to_string(),
1612+
Applicability::MaybeIncorrect,
1613+
);
1614+
}
1615+
}
1616+
}
1617+
}
1618+
}
1619+
15501620
/// When encountering a case where `.as_ref()` on a `Result` or `Option` would be appropriate,
15511621
/// suggests it.
15521622
fn suggest_as_ref_where_appropriate(

src/librustc_typeck/check/_match.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2828
};
2929

3030
// Type check the descriminant and get its type.
31-
let scrut_ty = if force_scrutinee_bool {
31+
let scrutinee_ty = if force_scrutinee_bool {
3232
// Here we want to ensure:
3333
//
3434
// 1. That default match bindings are *not* accepted in the condition of an
@@ -55,7 +55,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
5555

5656
// #55810: Type check patterns first so we get types for all bindings.
5757
for arm in arms {
58-
self.check_pat_top(&arm.pat, scrut_ty, Some(scrut.span), true);
58+
self.check_pat_top(&arm.pat, scrutinee_ty, Some(scrut.span), true);
5959
}
6060

6161
// Now typecheck the blocks.

src/librustc_typeck/check/expr.rs

+7-10
Original file line numberDiff line numberDiff line change
@@ -1518,7 +1518,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
15181518
def_id: DefId,
15191519
) {
15201520
let param_env = self.tcx().param_env(def_id);
1521-
let future_trait = self.tcx.require_lang_item(lang_items::FutureTraitLangItem, None);
1521+
let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
15221522
// Future::Output
15231523
let item_def_id =
15241524
self.tcx.associated_items(future_trait).in_definition_order().next().unwrap().def_id;
@@ -1554,15 +1554,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
15541554
);
15551555
if let ty::Adt(def, _) = normalized_ty.kind {
15561556
if def.non_enum_variant().fields.iter().any(|field| field.ident == field_ident) {
1557-
if let Ok(base) = self.tcx.sess.source_map().span_to_snippet(base.span) {
1558-
let suggestion = format!("{}.await.{}", base, field_ident);
1559-
err.span_suggestion(
1560-
expr.span,
1561-
"consider await before field access",
1562-
suggestion,
1563-
Applicability::MaybeIncorrect,
1564-
);
1565-
}
1557+
err.span_suggestion_verbose(
1558+
base.span.shrink_to_hi(),
1559+
"consider awaiting before field access",
1560+
".await".to_string(),
1561+
Applicability::MaybeIncorrect,
1562+
);
15661563
}
15671564
}
15681565
}

src/librustc_typeck/check/method/suggest.rs

+7-9
Original file line numberDiff line numberDiff line change
@@ -911,15 +911,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
911911
);
912912
let method_exists = self.method_exists(item_name, normalized_ty, call.hir_id, true);
913913
debug!("suggest_await_before_method: is_method_exist={}", method_exists);
914-
if let Ok(sp) = self.tcx.sess.source_map().span_to_snippet(span) {
915-
if method_exists {
916-
err.span_suggestion(
917-
span,
918-
"consider await before this method call",
919-
format!("await.{}", sp),
920-
Applicability::MaybeIncorrect,
921-
);
922-
}
914+
if method_exists {
915+
err.span_suggestion_verbose(
916+
span.shrink_to_lo(),
917+
"consider awaiting before this method call",
918+
"await.".to_string(),
919+
Applicability::MaybeIncorrect,
920+
);
923921
}
924922
}
925923
}

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

+5
Original file line numberDiff line numberDiff line change
@@ -64,5 +64,10 @@ async fn baz() -> Result<(), ()> {
6464
Ok(())
6565
}
6666

67+
async fn match_() {
68+
match tuple() {
69+
Tuple(_) => {} //~ ERROR mismatched types
70+
}
71+
}
6772

6873
fn main() {}

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

+35-11
Original file line numberDiff line numberDiff line change
@@ -26,28 +26,52 @@ error[E0609]: no field `0` on type `impl std::future::Future`
2626
--> $DIR/issue-61076.rs:58:26
2727
|
2828
LL | let _: i32 = tuple().0;
29-
| --------^
30-
| |
31-
| help: consider await before field access: `tuple().await.0`
29+
| ^
30+
|
31+
help: consider awaiting before field access
32+
|
33+
LL | let _: i32 = tuple().await.0;
34+
| ^^^^^^
3235

3336
error[E0609]: no field `a` on type `impl std::future::Future`
3437
--> $DIR/issue-61076.rs:60:28
3538
|
3639
LL | let _: i32 = struct_().a;
37-
| ----------^
38-
| |
39-
| help: consider await before field access: `struct_().await.a`
40+
| ^
41+
|
42+
help: consider awaiting before field access
43+
|
44+
LL | let _: i32 = struct_().await.a;
45+
| ^^^^^^
4046

4147
error[E0599]: no method named `method` found for opaque type `impl std::future::Future` in the current scope
4248
--> $DIR/issue-61076.rs:62:15
4349
|
4450
LL | struct_().method();
51+
| ^^^^^^ method not found in `impl std::future::Future`
52+
|
53+
help: consider awaiting before this method call
54+
|
55+
LL | struct_().await.method();
4556
| ^^^^^^
46-
| |
47-
| method not found in `impl std::future::Future`
48-
| help: consider await before this method call: `await.method`
4957

50-
error: aborting due to 5 previous errors
58+
error[E0308]: mismatched types
59+
--> $DIR/issue-61076.rs:69:9
60+
|
61+
LL | async fn tuple() -> Tuple {
62+
| ----- the `Output` of this `async fn`'s expected opaque type
63+
...
64+
LL | Tuple(_) => {}
65+
| ^^^^^^^^ expected opaque type, found struct `Tuple`
66+
|
67+
= note: expected opaque type `impl std::future::Future`
68+
found struct `Tuple`
69+
help: consider awaiting on the future
70+
|
71+
LL | match tuple().await {
72+
| ^^^^^^
73+
74+
error: aborting due to 6 previous errors
5175

52-
Some errors have detailed explanations: E0277, E0599, E0609.
76+
Some errors have detailed explanations: E0277, E0308, E0599, E0609.
5377
For more information about an error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)