Skip to content

Commit c548511

Browse files
committed
Add more .await suggestions on E0308
1 parent 1829b4a commit c548511

File tree

12 files changed

+167
-188
lines changed

12 files changed

+167
-188
lines changed

Diff for: compiler/rustc_infer/src/infer/error_reporting/mod.rs

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

5252
use crate::infer;
53-
use crate::infer::OriginalQueryValues;
5453
use crate::traits::error_reporting::report_object_safety_error;
5554
use crate::traits::{
5655
IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
@@ -65,7 +64,6 @@ use rustc_hir::def_id::DefId;
6564
use rustc_hir::lang_items::LangItem;
6665
use rustc_hir::{Item, ItemKind, Node};
6766
use rustc_middle::ty::error::TypeError;
68-
use rustc_middle::ty::ParamEnvAnd;
6967
use rustc_middle::ty::{
7068
self,
7169
subst::{Subst, SubstsRef},
@@ -1621,6 +1619,16 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
16211619
Mismatch::Variable(exp_found) => Some(exp_found),
16221620
Mismatch::Fixed(_) => None,
16231621
};
1622+
let exp_found = match terr {
1623+
// `terr` has more accurate type information than `exp_found` in match expressions.
1624+
ty::error::TypeError::Sorts(terr)
1625+
if exp_found.map_or(false, |ef| terr.found == ef.found) =>
1626+
{
1627+
Some(*terr)
1628+
}
1629+
_ => exp_found,
1630+
};
1631+
debug!("exp_found {:?} terr {:?}", exp_found, terr);
16241632
if let Some(exp_found) = exp_found {
16251633
self.suggest_as_ref_where_appropriate(span, &exp_found, diag);
16261634
self.suggest_await_on_expect_found(cause, span, &exp_found, diag);
@@ -1642,6 +1650,53 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
16421650
self.note_error_origin(diag, cause, exp_found);
16431651
}
16441652

1653+
fn get_impl_future_output_ty(&self, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
1654+
if let ty::Opaque(def_id, substs) = ty.kind() {
1655+
let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
1656+
// Future::Output
1657+
let item_def_id = self
1658+
.tcx
1659+
.associated_items(future_trait)
1660+
.in_definition_order()
1661+
.next()
1662+
.unwrap()
1663+
.def_id;
1664+
1665+
let bounds = self.tcx.explicit_item_bounds(*def_id);
1666+
1667+
for (predicate, _) in bounds {
1668+
let predicate = predicate.subst(self.tcx, substs);
1669+
if let ty::PredicateAtom::Projection(projection_predicate) =
1670+
predicate.skip_binders()
1671+
{
1672+
if projection_predicate.projection_ty.item_def_id == item_def_id {
1673+
// We don't account for multiple `Future::Output = Ty` contraints.
1674+
return Some(projection_predicate.ty);
1675+
}
1676+
}
1677+
}
1678+
}
1679+
None
1680+
}
1681+
1682+
/// A possible error is to forget to add `.await` when using futures:
1683+
///
1684+
/// ```
1685+
/// async fn make_u32() -> u32 {
1686+
/// 22
1687+
/// }
1688+
///
1689+
/// fn take_u32(x: u32) {}
1690+
///
1691+
/// async fn foo() {
1692+
/// let x = make_u32();
1693+
/// take_u32(x);
1694+
/// }
1695+
/// ```
1696+
///
1697+
/// This routine checks if the found type `T` implements `Future<Output=U>` where `U` is the
1698+
/// expected type. If this is the case, and we are inside of an async body, it suggests adding
1699+
/// `.await` to the tail of the expression.
16451700
fn suggest_await_on_expect_found(
16461701
&self,
16471702
cause: &ObligationCause<'tcx>,
@@ -1651,50 +1706,76 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
16511706
) {
16521707
debug!(
16531708
"suggest_await_on_expect_found: exp_span={:?}, expected_ty={:?}, found_ty={:?}",
1654-
exp_span, exp_found.expected, exp_found.found
1709+
exp_span, exp_found.expected, exp_found.found,
16551710
);
16561711

1657-
if let ty::Opaque(def_id, _) = *exp_found.expected.kind() {
1658-
let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
1659-
// Future::Output
1660-
let item_def_id = self
1661-
.tcx
1662-
.associated_items(future_trait)
1663-
.in_definition_order()
1664-
.next()
1665-
.unwrap()
1666-
.def_id;
1712+
if let ObligationCauseCode::CompareImplMethodObligation { .. } = &cause.code {
1713+
return;
1714+
}
16671715

1668-
let projection_ty = self.tcx.projection_ty_from_predicates((def_id, item_def_id));
1669-
if let Some(projection_ty) = projection_ty {
1670-
let projection_query = self.canonicalize_query(
1671-
&ParamEnvAnd { param_env: self.tcx.param_env(def_id), value: projection_ty },
1672-
&mut OriginalQueryValues::default(),
1673-
);
1674-
if let Ok(resp) = self.tcx.normalize_projection_ty(projection_query) {
1675-
let normalized_ty = resp.value.value.normalized_ty;
1676-
debug!("suggest_await_on_expect_found: normalized={:?}", normalized_ty);
1677-
if ty::TyS::same_type(normalized_ty, exp_found.found) {
1678-
let span = if let ObligationCauseCode::Pattern {
1679-
span,
1680-
origin_expr: _,
1681-
root_ty: _,
1682-
} = cause.code
1683-
{
1684-
// scrutinee's span
1685-
span.unwrap_or(exp_span)
1686-
} else {
1687-
exp_span
1688-
};
1689-
diag.span_suggestion_verbose(
1690-
span.shrink_to_hi(),
1691-
"consider awaiting on the future",
1692-
".await".to_string(),
1716+
match (
1717+
self.get_impl_future_output_ty(exp_found.expected),
1718+
self.get_impl_future_output_ty(exp_found.found),
1719+
) {
1720+
(Some(exp), Some(found)) if ty::TyS::same_type(exp, found) => match &cause.code {
1721+
ObligationCauseCode::IfExpression(box IfExpressionCause { then, .. }) => {
1722+
diag.multipart_suggestion(
1723+
"consider `await`ing on both `Future`s",
1724+
vec![
1725+
(then.shrink_to_hi(), ".await".to_string()),
1726+
(exp_span.shrink_to_hi(), ".await".to_string()),
1727+
],
1728+
Applicability::MaybeIncorrect,
1729+
);
1730+
}
1731+
ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
1732+
prior_arms,
1733+
..
1734+
}) => {
1735+
if let [.., arm_span] = &prior_arms[..] {
1736+
diag.multipart_suggestion(
1737+
"consider `await`ing on both `Future`s",
1738+
vec![
1739+
(arm_span.shrink_to_hi(), ".await".to_string()),
1740+
(exp_span.shrink_to_hi(), ".await".to_string()),
1741+
],
16931742
Applicability::MaybeIncorrect,
16941743
);
1744+
} else {
1745+
diag.help("consider `await`ing on both `Future`s");
16951746
}
16961747
}
1748+
_ => {
1749+
diag.help("consider `await`ing on both `Future`s");
1750+
}
1751+
},
1752+
(_, Some(ty)) if ty::TyS::same_type(exp_found.expected, ty) => {
1753+
let span = match cause.code {
1754+
// scrutinee's span
1755+
ObligationCauseCode::Pattern { span: Some(span), .. } => span,
1756+
_ => exp_span,
1757+
};
1758+
diag.span_suggestion_verbose(
1759+
span.shrink_to_hi(),
1760+
"consider `await`ing on the `Future`",
1761+
".await".to_string(),
1762+
Applicability::MaybeIncorrect,
1763+
);
1764+
}
1765+
(Some(ty), _) if ty::TyS::same_type(ty, exp_found.found) => {
1766+
let span = match cause.code {
1767+
// scrutinee's span
1768+
ObligationCauseCode::Pattern { span: Some(span), .. } => span,
1769+
_ => exp_span,
1770+
};
1771+
diag.span_suggestion_verbose(
1772+
span.shrink_to_hi(),
1773+
"consider `await`ing on the `Future`",
1774+
".await".to_string(),
1775+
Applicability::MaybeIncorrect,
1776+
);
16971777
}
1778+
_ => {}
16981779
}
16991780
}
17001781

Diff for: compiler/rustc_middle/src/ty/error.rs

+13-24
Original file line numberDiff line numberDiff line change
@@ -334,26 +334,15 @@ impl<'tcx> TyCtxt<'tcx> {
334334
debug!("note_and_explain_type_err err={:?} cause={:?}", err, cause);
335335
match err {
336336
Sorts(values) => {
337-
let expected_str = values.expected.sort_string(self);
338-
let found_str = values.found.sort_string(self);
339-
if expected_str == found_str && expected_str == "closure" {
340-
db.note("no two closures, even if identical, have the same type");
341-
db.help("consider boxing your closure and/or using it as a trait object");
342-
}
343-
if expected_str == found_str && expected_str == "opaque type" {
344-
// Issue #63167
345-
db.note("distinct uses of `impl Trait` result in different opaque types");
346-
let e_str = values.expected.to_string();
347-
let f_str = values.found.to_string();
348-
if e_str == f_str && &e_str == "impl std::future::Future" {
349-
// FIXME: use non-string based check.
350-
db.help(
351-
"if both `Future`s have the same `Output` type, consider \
352-
`.await`ing on both of them",
353-
);
354-
}
355-
}
356337
match (values.expected.kind(), values.found.kind()) {
338+
(ty::Closure(..), ty::Closure(..)) => {
339+
db.note("no two closures, even if identical, have the same type");
340+
db.help("consider boxing your closure and/or using it as a trait object");
341+
}
342+
(ty::Opaque(..), ty::Opaque(..)) => {
343+
// Issue #63167
344+
db.note("distinct uses of `impl Trait` result in different opaque types");
345+
}
357346
(ty::Float(_), ty::Infer(ty::IntVar(_))) => {
358347
if let Ok(
359348
// Issue #53280
@@ -382,12 +371,12 @@ impl<'tcx> TyCtxt<'tcx> {
382371
}
383372
db.note(
384373
"a type parameter was expected, but a different one was found; \
385-
you might be missing a type parameter or trait bound",
374+
you might be missing a type parameter or trait bound",
386375
);
387376
db.note(
388377
"for more information, visit \
389-
https://doc.rust-lang.org/book/ch10-02-traits.html\
390-
#traits-as-parameters",
378+
https://doc.rust-lang.org/book/ch10-02-traits.html\
379+
#traits-as-parameters",
391380
);
392381
}
393382
(ty::Projection(_), ty::Projection(_)) => {
@@ -471,8 +460,8 @@ impl<T> Trait<T> for X {
471460
}
472461
db.note(
473462
"for more information, visit \
474-
https://doc.rust-lang.org/book/ch10-02-traits.html\
475-
#traits-as-parameters",
463+
https://doc.rust-lang.org/book/ch10-02-traits.html\
464+
#traits-as-parameters",
476465
);
477466
}
478467
(ty::Param(p), ty::Closure(..) | ty::Generator(..)) => {

Diff for: compiler/rustc_typeck/src/check/demand.rs

-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
3333
return;
3434
}
3535
self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty);
36-
self.suggest_missing_await(err, expr, expected, expr_ty);
3736
self.suggest_missing_parentheses(err, expr);
3837
self.note_need_for_fn_pointer(err, expected, expr_ty);
3938
self.note_internal_mutation_in_method(err, expr, expected, expr_ty);

Diff for: compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs

-79
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use crate::astconv::AstConv;
33

44
use rustc_ast::util::parser::ExprPrecedence;
55
use rustc_span::{self, Span};
6-
use rustc_trait_selection::traits;
76

87
use rustc_errors::{Applicability, DiagnosticBuilder};
98
use rustc_hir as hir;
@@ -13,7 +12,6 @@ use rustc_hir::{ExprKind, ItemKind, Node};
1312
use rustc_infer::infer;
1413
use rustc_middle::ty::{self, Ty};
1514
use rustc_span::symbol::kw;
16-
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
1715

1816
use std::iter;
1917

@@ -433,83 +431,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
433431
}
434432
}
435433

436-
/// A possible error is to forget to add `.await` when using futures:
437-
///
438-
/// ```
439-
/// async fn make_u32() -> u32 {
440-
/// 22
441-
/// }
442-
///
443-
/// fn take_u32(x: u32) {}
444-
///
445-
/// async fn foo() {
446-
/// let x = make_u32();
447-
/// take_u32(x);
448-
/// }
449-
/// ```
450-
///
451-
/// This routine checks if the found type `T` implements `Future<Output=U>` where `U` is the
452-
/// expected type. If this is the case, and we are inside of an async body, it suggests adding
453-
/// `.await` to the tail of the expression.
454-
pub(in super::super) fn suggest_missing_await(
455-
&self,
456-
err: &mut DiagnosticBuilder<'_>,
457-
expr: &hir::Expr<'_>,
458-
expected: Ty<'tcx>,
459-
found: Ty<'tcx>,
460-
) {
461-
debug!("suggest_missing_await: expr={:?} expected={:?}, found={:?}", expr, expected, found);
462-
// `.await` is not permitted outside of `async` bodies, so don't bother to suggest if the
463-
// body isn't `async`.
464-
let item_id = self.tcx().hir().get_parent_node(self.body_id);
465-
if let Some(body_id) = self.tcx().hir().maybe_body_owned_by(item_id) {
466-
let body = self.tcx().hir().body(body_id);
467-
if let Some(hir::GeneratorKind::Async(_)) = body.generator_kind {
468-
let sp = expr.span;
469-
// Check for `Future` implementations by constructing a predicate to
470-
// prove: `<T as Future>::Output == U`
471-
let future_trait = self.tcx.require_lang_item(LangItem::Future, Some(sp));
472-
let item_def_id = self
473-
.tcx
474-
.associated_items(future_trait)
475-
.in_definition_order()
476-
.next()
477-
.unwrap()
478-
.def_id;
479-
// `<T as Future>::Output`
480-
let projection_ty = ty::ProjectionTy {
481-
// `T`
482-
substs: self
483-
.tcx
484-
.mk_substs_trait(found, self.fresh_substs_for_item(sp, item_def_id)),
485-
// `Future::Output`
486-
item_def_id,
487-
};
488-
489-
let predicate = ty::PredicateAtom::Projection(ty::ProjectionPredicate {
490-
projection_ty,
491-
ty: expected,
492-
})
493-
.potentially_quantified(self.tcx, ty::PredicateKind::ForAll);
494-
let obligation = traits::Obligation::new(self.misc(sp), self.param_env, predicate);
495-
496-
debug!("suggest_missing_await: trying obligation {:?}", obligation);
497-
498-
if self.infcx.predicate_may_hold(&obligation) {
499-
debug!("suggest_missing_await: obligation held: {:?}", obligation);
500-
err.span_suggestion_verbose(
501-
sp.shrink_to_hi(),
502-
"consider `await`ing on the `Future`",
503-
".await".to_string(),
504-
Applicability::MaybeIncorrect,
505-
);
506-
} else {
507-
debug!("suggest_missing_await: obligation did not hold: {:?}", obligation)
508-
}
509-
}
510-
}
511-
}
512-
513434
pub(in super::super) fn suggest_missing_parentheses(
514435
&self,
515436
err: &mut DiagnosticBuilder<'_>,

Diff for: src/test/ui/async-await/dont-suggest-missing-await.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ LL | take_u32(x)
99
|
1010
= note: expected type `u32`
1111
found opaque type `impl Future`
12+
help: consider `await`ing on the `Future`
13+
|
14+
LL | take_u32(x.await)
15+
| ^^^^^^
1216

1317
error: aborting due to previous error
1418

Diff for: src/test/ui/async-await/issue-61076.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ LL | Tuple(_) => {}
5353
|
5454
= note: expected opaque type `impl Future`
5555
found struct `Tuple`
56+
help: consider `await`ing on the `Future`
57+
|
58+
LL | match tuple().await {
59+
| ^^^^^^
5660

5761
error: aborting due to 6 previous errors
5862

0 commit comments

Comments
 (0)