about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDylan DPC <dylan.dpc@gmail.com>2020-10-26 03:09:06 +0100
committerGitHub <noreply@github.com>2020-10-26 03:09:06 +0100
commit083a5cd9a2cb087e9acd7c9e80e277883f7003b3 (patch)
tree1d6b485d650276f49b81948719e3f499d6ba83a2
parent9e907d420e44cd91bf0a5ee421be26f537261a22 (diff)
parentf5d7443a6bd90b78e61b9c47e5032b5e1be1e49f (diff)
downloadrust-083a5cd9a2cb087e9acd7c9e80e277883f7003b3.tar.gz
rust-083a5cd9a2cb087e9acd7c9e80e277883f7003b3.zip
Rollup merge of #78214 - estebank:match-semicolon, r=oli-obk
Tweak match arm semicolon removal suggestion to account for futures

* Tweak and extend "use `.await`" suggestions
* Suggest removal of semicolon on prior match arm
* Account for `impl Future` when suggesting semicolon removal
* Silence some errors when encountering `await foo()?` as can't be certain what the intent was

*Thanks to https://twitter.com/a_hoverbear/status/1318960787105353728 for pointing this out!*
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs223
-rw-r--r--compiler/rustc_middle/src/traits/mod.rs17
-rw-r--r--compiler/rustc_middle/src/ty/error.rs37
-rw-r--r--compiler/rustc_parse/src/parser/diagnostics.rs8
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs10
-rw-r--r--compiler/rustc_typeck/src/check/_match.rs32
-rw-r--r--compiler/rustc_typeck/src/check/demand.rs1
-rw-r--r--compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs57
-rw-r--r--compiler/rustc_typeck/src/check/fn_ctxt/checks.rs25
-rw-r--r--compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs83
-rw-r--r--src/test/ui/async-await/await-keyword/incorrect-syntax-suggestions.rs5
-rw-r--r--src/test/ui/async-await/await-keyword/incorrect-syntax-suggestions.stderr94
-rw-r--r--src/test/ui/async-await/dont-suggest-missing-await.stderr4
-rw-r--r--src/test/ui/async-await/issue-61076.stderr22
-rw-r--r--src/test/ui/async-await/suggest-missing-await-closure.fixed4
-rw-r--r--src/test/ui/async-await/suggest-missing-await-closure.rs4
-rw-r--r--src/test/ui/async-await/suggest-missing-await-closure.stderr9
-rw-r--r--src/test/ui/async-await/suggest-missing-await.fixed30
-rw-r--r--src/test/ui/async-await/suggest-missing-await.rs9
-rw-r--r--src/test/ui/async-await/suggest-missing-await.stderr21
-rw-r--r--src/test/ui/suggestions/issue-72766.stderr9
-rw-r--r--src/test/ui/suggestions/match-prev-arm-needing-semi.rs57
-rw-r--r--src/test/ui/suggestions/match-prev-arm-needing-semi.stderr118
-rw-r--r--src/test/ui/suggestions/opaque-type-error.stderr6
24 files changed, 567 insertions, 318 deletions
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index 3a0ec6327c1..f7e4ace8fc5 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -50,10 +50,10 @@ use super::region_constraints::GenericKind;
 use super::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TypeTrace, ValuePairs};
 
 use crate::infer;
-use crate::infer::OriginalQueryValues;
 use crate::traits::error_reporting::report_object_safety_error;
 use crate::traits::{
     IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
+    StatementAsExpression,
 };
 
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
@@ -64,7 +64,6 @@ use rustc_hir::def_id::DefId;
 use rustc_hir::lang_items::LangItem;
 use rustc_hir::{Item, ItemKind, Node};
 use rustc_middle::ty::error::TypeError;
-use rustc_middle::ty::ParamEnvAnd;
 use rustc_middle::ty::{
     self,
     subst::{Subst, SubstsRef},
@@ -688,13 +687,36 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                     };
                     let msg = "`match` arms have incompatible types";
                     err.span_label(outer_error_span, msg);
-                    if let Some(sp) = semi_span {
-                        err.span_suggestion_short(
-                            sp,
-                            "consider removing this semicolon",
-                            String::new(),
-                            Applicability::MachineApplicable,
-                        );
+                    if let Some((sp, boxed)) = semi_span {
+                        if let (StatementAsExpression::NeedsBoxing, [.., prior_arm]) =
+                            (boxed, &prior_arms[..])
+                        {
+                            err.multipart_suggestion(
+                                "consider removing this semicolon and boxing the expressions",
+                                vec![
+                                    (prior_arm.shrink_to_lo(), "Box::new(".to_string()),
+                                    (prior_arm.shrink_to_hi(), ")".to_string()),
+                                    (arm_span.shrink_to_lo(), "Box::new(".to_string()),
+                                    (arm_span.shrink_to_hi(), ")".to_string()),
+                                    (sp, String::new()),
+                                ],
+                                Applicability::HasPlaceholders,
+                            );
+                        } else if matches!(boxed, StatementAsExpression::NeedsBoxing) {
+                            err.span_suggestion_short(
+                                sp,
+                                "consider removing this semicolon and boxing the expressions",
+                                String::new(),
+                                Applicability::MachineApplicable,
+                            );
+                        } else {
+                            err.span_suggestion_short(
+                                sp,
+                                "consider removing this semicolon",
+                                String::new(),
+                                Applicability::MachineApplicable,
+                            );
+                        }
                     }
                     if let Some(ret_sp) = opt_suggest_box_span {
                         // Get return type span and point to it.
@@ -717,13 +739,27 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                 if let Some(sp) = outer {
                     err.span_label(sp, "`if` and `else` have incompatible types");
                 }
-                if let Some(sp) = semicolon {
-                    err.span_suggestion_short(
-                        sp,
-                        "consider removing this semicolon",
-                        String::new(),
-                        Applicability::MachineApplicable,
-                    );
+                if let Some((sp, boxed)) = semicolon {
+                    if matches!(boxed, StatementAsExpression::NeedsBoxing) {
+                        err.multipart_suggestion(
+                            "consider removing this semicolon and boxing the expression",
+                            vec![
+                                (then.shrink_to_lo(), "Box::new(".to_string()),
+                                (then.shrink_to_hi(), ")".to_string()),
+                                (else_sp.shrink_to_lo(), "Box::new(".to_string()),
+                                (else_sp.shrink_to_hi(), ")".to_string()),
+                                (sp, String::new()),
+                            ],
+                            Applicability::MachineApplicable,
+                        );
+                    } else {
+                        err.span_suggestion_short(
+                            sp,
+                            "consider removing this semicolon",
+                            String::new(),
+                            Applicability::MachineApplicable,
+                        );
+                    }
                 }
                 if let Some(ret_sp) = opt_suggest_box_span {
                     self.suggest_boxing_for_return_impl_trait(
@@ -1602,6 +1638,16 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
             Mismatch::Variable(exp_found) => Some(exp_found),
             Mismatch::Fixed(_) => None,
         };
+        let exp_found = match terr {
+            // `terr` has more accurate type information than `exp_found` in match expressions.
+            ty::error::TypeError::Sorts(terr)
+                if exp_found.map_or(false, |ef| terr.found == ef.found) =>
+            {
+                Some(*terr)
+            }
+            _ => exp_found,
+        };
+        debug!("exp_found {:?} terr {:?}", exp_found, terr);
         if let Some(exp_found) = exp_found {
             self.suggest_as_ref_where_appropriate(span, &exp_found, diag);
             self.suggest_await_on_expect_found(cause, span, &exp_found, diag);
@@ -1623,6 +1669,53 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         self.note_error_origin(diag, cause, exp_found);
     }
 
+    fn get_impl_future_output_ty(&self, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
+        if let ty::Opaque(def_id, substs) = ty.kind() {
+            let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
+            // Future::Output
+            let item_def_id = self
+                .tcx
+                .associated_items(future_trait)
+                .in_definition_order()
+                .next()
+                .unwrap()
+                .def_id;
+
+            let bounds = self.tcx.explicit_item_bounds(*def_id);
+
+            for (predicate, _) in bounds {
+                let predicate = predicate.subst(self.tcx, substs);
+                if let ty::PredicateAtom::Projection(projection_predicate) =
+                    predicate.skip_binders()
+                {
+                    if projection_predicate.projection_ty.item_def_id == item_def_id {
+                        // We don't account for multiple `Future::Output = Ty` contraints.
+                        return Some(projection_predicate.ty);
+                    }
+                }
+            }
+        }
+        None
+    }
+
+    /// A possible error is to forget to add `.await` when using futures:
+    ///
+    /// ```
+    /// async fn make_u32() -> u32 {
+    ///     22
+    /// }
+    ///
+    /// fn take_u32(x: u32) {}
+    ///
+    /// async fn foo() {
+    ///     let x = make_u32();
+    ///     take_u32(x);
+    /// }
+    /// ```
+    ///
+    /// This routine checks if the found type `T` implements `Future<Output=U>` where `U` is the
+    /// expected type. If this is the case, and we are inside of an async body, it suggests adding
+    /// `.await` to the tail of the expression.
     fn suggest_await_on_expect_found(
         &self,
         cause: &ObligationCause<'tcx>,
@@ -1632,50 +1725,76 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
     ) {
         debug!(
             "suggest_await_on_expect_found: exp_span={:?}, expected_ty={:?}, found_ty={:?}",
-            exp_span, exp_found.expected, exp_found.found
+            exp_span, exp_found.expected, exp_found.found,
         );
 
-        if let ty::Opaque(def_id, _) = *exp_found.expected.kind() {
-            let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
-            // Future::Output
-            let item_def_id = self
-                .tcx
-                .associated_items(future_trait)
-                .in_definition_order()
-                .next()
-                .unwrap()
-                .def_id;
+        if let ObligationCauseCode::CompareImplMethodObligation { .. } = &cause.code {
+            return;
+        }
 
-            let projection_ty = self.tcx.projection_ty_from_predicates((def_id, item_def_id));
-            if let Some(projection_ty) = projection_ty {
-                let projection_query = self.canonicalize_query(
-                    &ParamEnvAnd { param_env: self.tcx.param_env(def_id), value: projection_ty },
-                    &mut OriginalQueryValues::default(),
-                );
-                if let Ok(resp) = self.tcx.normalize_projection_ty(projection_query) {
-                    let normalized_ty = resp.value.value.normalized_ty;
-                    debug!("suggest_await_on_expect_found: normalized={:?}", normalized_ty);
-                    if ty::TyS::same_type(normalized_ty, exp_found.found) {
-                        let span = if let ObligationCauseCode::Pattern {
-                            span,
-                            origin_expr: _,
-                            root_ty: _,
-                        } = cause.code
-                        {
-                            // scrutinee's span
-                            span.unwrap_or(exp_span)
-                        } else {
-                            exp_span
-                        };
-                        diag.span_suggestion_verbose(
-                            span.shrink_to_hi(),
-                            "consider awaiting on the future",
-                            ".await".to_string(),
+        match (
+            self.get_impl_future_output_ty(exp_found.expected),
+            self.get_impl_future_output_ty(exp_found.found),
+        ) {
+            (Some(exp), Some(found)) if ty::TyS::same_type(exp, found) => match &cause.code {
+                ObligationCauseCode::IfExpression(box IfExpressionCause { then, .. }) => {
+                    diag.multipart_suggestion(
+                        "consider `await`ing on both `Future`s",
+                        vec![
+                            (then.shrink_to_hi(), ".await".to_string()),
+                            (exp_span.shrink_to_hi(), ".await".to_string()),
+                        ],
+                        Applicability::MaybeIncorrect,
+                    );
+                }
+                ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
+                    prior_arms,
+                    ..
+                }) => {
+                    if let [.., arm_span] = &prior_arms[..] {
+                        diag.multipart_suggestion(
+                            "consider `await`ing on both `Future`s",
+                            vec![
+                                (arm_span.shrink_to_hi(), ".await".to_string()),
+                                (exp_span.shrink_to_hi(), ".await".to_string()),
+                            ],
                             Applicability::MaybeIncorrect,
                         );
+                    } else {
+                        diag.help("consider `await`ing on both `Future`s");
                     }
                 }
+                _ => {
+                    diag.help("consider `await`ing on both `Future`s");
+                }
+            },
+            (_, Some(ty)) if ty::TyS::same_type(exp_found.expected, ty) => {
+                let span = match cause.code {
+                    // scrutinee's span
+                    ObligationCauseCode::Pattern { span: Some(span), .. } => span,
+                    _ => exp_span,
+                };
+                diag.span_suggestion_verbose(
+                    span.shrink_to_hi(),
+                    "consider `await`ing on the `Future`",
+                    ".await".to_string(),
+                    Applicability::MaybeIncorrect,
+                );
+            }
+            (Some(ty), _) if ty::TyS::same_type(ty, exp_found.found) => {
+                let span = match cause.code {
+                    // scrutinee's span
+                    ObligationCauseCode::Pattern { span: Some(span), .. } => span,
+                    _ => exp_span,
+                };
+                diag.span_suggestion_verbose(
+                    span.shrink_to_hi(),
+                    "consider `await`ing on the `Future`",
+                    ".await".to_string(),
+                    Applicability::MaybeIncorrect,
+                );
             }
+            _ => {}
         }
     }
 
diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs
index bbc46b8d608..4deb7225dcb 100644
--- a/compiler/rustc_middle/src/traits/mod.rs
+++ b/compiler/rustc_middle/src/traits/mod.rs
@@ -340,11 +340,24 @@ impl ObligationCauseCode<'_> {
 #[cfg(target_arch = "x86_64")]
 static_assert_size!(ObligationCauseCode<'_>, 32);
 
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+pub enum StatementAsExpression {
+    CorrectType,
+    NeedsBoxing,
+}
+
+impl<'tcx> ty::Lift<'tcx> for StatementAsExpression {
+    type Lifted = StatementAsExpression;
+    fn lift_to_tcx(self, _tcx: TyCtxt<'tcx>) -> Option<StatementAsExpression> {
+        Some(self)
+    }
+}
+
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Lift)]
 pub struct MatchExpressionArmCause<'tcx> {
     pub arm_span: Span,
     pub scrut_span: Span,
-    pub semi_span: Option<Span>,
+    pub semi_span: Option<(Span, StatementAsExpression)>,
     pub source: hir::MatchSource,
     pub prior_arms: Vec<Span>,
     pub last_ty: Ty<'tcx>,
@@ -357,7 +370,7 @@ pub struct IfExpressionCause {
     pub then: Span,
     pub else_sp: Span,
     pub outer: Option<Span>,
-    pub semicolon: Option<Span>,
+    pub semicolon: Option<(Span, StatementAsExpression)>,
     pub opt_suggest_box_span: Option<Span>,
 }
 
diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs
index 235f8749cf9..5ec0ec0c56a 100644
--- a/compiler/rustc_middle/src/ty/error.rs
+++ b/compiler/rustc_middle/src/ty/error.rs
@@ -334,26 +334,15 @@ impl<'tcx> TyCtxt<'tcx> {
         debug!("note_and_explain_type_err err={:?} cause={:?}", err, cause);
         match err {
             Sorts(values) => {
-                let expected_str = values.expected.sort_string(self);
-                let found_str = values.found.sort_string(self);
-                if expected_str == found_str && expected_str == "closure" {
-                    db.note("no two closures, even if identical, have the same type");
-                    db.help("consider boxing your closure and/or using it as a trait object");
-                }
-                if expected_str == found_str && expected_str == "opaque type" {
-                    // Issue #63167
-                    db.note("distinct uses of `impl Trait` result in different opaque types");
-                    let e_str = values.expected.to_string();
-                    let f_str = values.found.to_string();
-                    if e_str == f_str && &e_str == "impl std::future::Future" {
-                        // FIXME: use non-string based check.
-                        db.help(
-                            "if both `Future`s have the same `Output` type, consider \
-                                 `.await`ing on both of them",
-                        );
-                    }
-                }
                 match (values.expected.kind(), values.found.kind()) {
+                    (ty::Closure(..), ty::Closure(..)) => {
+                        db.note("no two closures, even if identical, have the same type");
+                        db.help("consider boxing your closure and/or using it as a trait object");
+                    }
+                    (ty::Opaque(..), ty::Opaque(..)) => {
+                        // Issue #63167
+                        db.note("distinct uses of `impl Trait` result in different opaque types");
+                    }
                     (ty::Float(_), ty::Infer(ty::IntVar(_))) => {
                         if let Ok(
                             // Issue #53280
@@ -382,12 +371,12 @@ impl<'tcx> TyCtxt<'tcx> {
                         }
                         db.note(
                             "a type parameter was expected, but a different one was found; \
-                                 you might be missing a type parameter or trait bound",
+                             you might be missing a type parameter or trait bound",
                         );
                         db.note(
                             "for more information, visit \
-                                 https://doc.rust-lang.org/book/ch10-02-traits.html\
-                                 #traits-as-parameters",
+                             https://doc.rust-lang.org/book/ch10-02-traits.html\
+                             #traits-as-parameters",
                         );
                     }
                     (ty::Projection(_), ty::Projection(_)) => {
@@ -471,8 +460,8 @@ impl<T> Trait<T> for X {
                         }
                         db.note(
                             "for more information, visit \
-                                 https://doc.rust-lang.org/book/ch10-02-traits.html\
-                                 #traits-as-parameters",
+                             https://doc.rust-lang.org/book/ch10-02-traits.html\
+                             #traits-as-parameters",
                         );
                     }
                     (ty::Param(p), ty::Closure(..) | ty::Generator(..)) => {
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs
index 1ea01d95a13..39e1256a578 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -1207,7 +1207,13 @@ impl<'a> Parser<'a> {
             self.recover_await_prefix(await_sp)?
         };
         let sp = self.error_on_incorrect_await(lo, hi, &expr, is_question);
-        let expr = self.mk_expr(lo.to(sp), ExprKind::Await(expr), attrs);
+        let kind = match expr.kind {
+            // Avoid knock-down errors as we don't know whether to interpret this as `foo().await?`
+            // or `foo()?.await` (the very reason we went with postfix syntax 😅).
+            ExprKind::Try(_) => ExprKind::Err,
+            _ => ExprKind::Await(expr),
+        };
+        let expr = self.mk_expr(lo.to(sp), kind, attrs);
         self.maybe_recover_from_bad_qpath(expr, true)
     }
 
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index efa9bd633ba..fa837e04db3 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -21,7 +21,7 @@ use rustc_middle::ty::{
 };
 use rustc_middle::ty::{TypeAndMut, TypeckResults};
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
-use rustc_span::{MultiSpan, Span, DUMMY_SP};
+use rustc_span::{BytePos, MultiSpan, Span, DUMMY_SP};
 use rustc_target::spec::abi;
 use std::fmt;
 
@@ -2114,10 +2114,10 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                 if self.predicate_may_hold(&try_obligation) && impls_future {
                     if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
                         if snippet.ends_with('?') {
-                            err.span_suggestion(
-                                span,
-                                "consider using `.await` here",
-                                format!("{}.await?", snippet.trim_end_matches('?')),
+                            err.span_suggestion_verbose(
+                                span.with_hi(span.hi() - BytePos(1)).shrink_to_hi(),
+                                "consider `await`ing on the `Future`",
+                                ".await".to_string(),
                                 Applicability::MaybeIncorrect,
                             );
                         }
diff --git a/compiler/rustc_typeck/src/check/_match.rs b/compiler/rustc_typeck/src/check/_match.rs
index 398e013e62f..e8eea65137f 100644
--- a/compiler/rustc_typeck/src/check/_match.rs
+++ b/compiler/rustc_typeck/src/check/_match.rs
@@ -9,6 +9,7 @@ use rustc_trait_selection::opaque_types::InferCtxtExt as _;
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
 use rustc_trait_selection::traits::{
     IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
+    StatementAsExpression,
 };
 
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
@@ -188,11 +189,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     }
                 }
             } else {
-                let (arm_span, semi_span) = if let hir::ExprKind::Block(blk, _) = &arm.body.kind {
-                    self.find_block_span(blk, prior_arm_ty)
-                } else {
-                    (arm.body.span, None)
-                };
+                let (arm_span, semi_span) =
+                    self.get_appropriate_arm_semicolon_removal_span(&arms, i, prior_arm_ty, arm_ty);
                 let (span, code) = match i {
                     // The reason for the first arm to fail is not that the match arms diverge,
                     // but rather that there's a prior obligation that doesn't hold.
@@ -242,6 +240,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         coercion.complete(self)
     }
 
+    fn get_appropriate_arm_semicolon_removal_span(
+        &self,
+        arms: &'tcx [hir::Arm<'tcx>],
+        i: usize,
+        prior_arm_ty: Option<Ty<'tcx>>,
+        arm_ty: Ty<'tcx>,
+    ) -> (Span, Option<(Span, StatementAsExpression)>) {
+        let arm = &arms[i];
+        let (arm_span, mut semi_span) = if let hir::ExprKind::Block(blk, _) = &arm.body.kind {
+            self.find_block_span(blk, prior_arm_ty)
+        } else {
+            (arm.body.span, None)
+        };
+        if semi_span.is_none() && i > 0 {
+            if let hir::ExprKind::Block(blk, _) = &arms[i - 1].body.kind {
+                let (_, semi_span_prev) = self.find_block_span(blk, Some(arm_ty));
+                semi_span = semi_span_prev;
+            }
+        }
+        (arm_span, semi_span)
+    }
+
     /// When the previously checked expression (the scrutinee) diverges,
     /// warn the user about the match arms being unreachable.
     fn warn_arms_when_scrutinee_diverges(
@@ -514,7 +534,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         &self,
         block: &'tcx hir::Block<'tcx>,
         expected_ty: Option<Ty<'tcx>>,
-    ) -> (Span, Option<Span>) {
+    ) -> (Span, Option<(Span, StatementAsExpression)>) {
         if let Some(expr) = &block.expr {
             (expr.span, None)
         } else if let Some(stmt) = block.stmts.last() {
diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs
index b8143787a2d..241803fab1e 100644
--- a/compiler/rustc_typeck/src/check/demand.rs
+++ b/compiler/rustc_typeck/src/check/demand.rs
@@ -33,7 +33,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             return;
         }
         self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty);
-        self.suggest_missing_await(err, expr, expected, expr_ty);
         self.suggest_missing_parentheses(err, expr);
         self.note_need_for_fn_pointer(err, expected, expr_ty);
         self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
index 017b0abd1d6..f87e6b607d4 100644
--- a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
+++ b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
@@ -33,7 +33,9 @@ use rustc_span::{self, BytePos, MultiSpan, Span};
 use rustc_trait_selection::infer::InferCtxtExt as _;
 use rustc_trait_selection::opaque_types::InferCtxtExt as _;
 use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
-use rustc_trait_selection::traits::{self, ObligationCauseCode, TraitEngine, TraitEngineExt};
+use rustc_trait_selection::traits::{
+    self, ObligationCauseCode, StatementAsExpression, TraitEngine, TraitEngineExt,
+};
 
 use std::collections::hash_map::Entry;
 use std::slice;
@@ -1061,7 +1063,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         &self,
         blk: &'tcx hir::Block<'tcx>,
         expected_ty: Ty<'tcx>,
-    ) -> Option<Span> {
+    ) -> Option<(Span, StatementAsExpression)> {
         // Be helpful when the user wrote `{... expr;}` and
         // taking the `;` off is enough to fix the error.
         let last_stmt = blk.stmts.last()?;
@@ -1070,13 +1072,58 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             _ => return None,
         };
         let last_expr_ty = self.node_ty(last_expr.hir_id);
-        if matches!(last_expr_ty.kind(), ty::Error(_))
-            || self.can_sub(self.param_env, last_expr_ty, expected_ty).is_err()
+        let needs_box = match (last_expr_ty.kind(), expected_ty.kind()) {
+            (ty::Opaque(last_def_id, last_bounds), ty::Opaque(exp_def_id, exp_bounds)) => {
+                debug!(
+                    "both opaque, likely future {:?} {:?} {:?} {:?}",
+                    last_def_id, last_bounds, exp_def_id, exp_bounds
+                );
+                let last_hir_id = self.tcx.hir().local_def_id_to_hir_id(last_def_id.expect_local());
+                let exp_hir_id = self.tcx.hir().local_def_id_to_hir_id(exp_def_id.expect_local());
+                match (
+                    &self.tcx.hir().expect_item(last_hir_id).kind,
+                    &self.tcx.hir().expect_item(exp_hir_id).kind,
+                ) {
+                    (
+                        hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: last_bounds, .. }),
+                        hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: exp_bounds, .. }),
+                    ) if last_bounds.iter().zip(exp_bounds.iter()).all(|(left, right)| {
+                        match (left, right) {
+                            (
+                                hir::GenericBound::Trait(tl, ml),
+                                hir::GenericBound::Trait(tr, mr),
+                            ) if tl.trait_ref.trait_def_id() == tr.trait_ref.trait_def_id()
+                                && ml == mr =>
+                            {
+                                true
+                            }
+                            (
+                                hir::GenericBound::LangItemTrait(langl, _, _, argsl),
+                                hir::GenericBound::LangItemTrait(langr, _, _, argsr),
+                            ) if langl == langr => {
+                                // FIXME: consider the bounds!
+                                debug!("{:?} {:?}", argsl, argsr);
+                                true
+                            }
+                            _ => false,
+                        }
+                    }) =>
+                    {
+                        StatementAsExpression::NeedsBoxing
+                    }
+                    _ => StatementAsExpression::CorrectType,
+                }
+            }
+            _ => StatementAsExpression::CorrectType,
+        };
+        if (matches!(last_expr_ty.kind(), ty::Error(_))
+            || self.can_sub(self.param_env, last_expr_ty, expected_ty).is_err())
+            && matches!(needs_box, StatementAsExpression::CorrectType)
         {
             return None;
         }
         let original_span = original_sp(last_stmt.span, blk.span);
-        Some(original_span.with_lo(original_span.hi() - BytePos(1)))
+        Some((original_span.with_lo(original_span.hi() - BytePos(1)), needs_box))
     }
 
     // Instantiates the given path, which must refer to an item with the given
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
index fd2f5eb5018..a820661d843 100644
--- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
+++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
@@ -20,7 +20,7 @@ use rustc_middle::ty::{self, Ty};
 use rustc_session::Session;
 use rustc_span::symbol::{sym, Ident};
 use rustc_span::{self, MultiSpan, Span};
-use rustc_trait_selection::traits::{self, ObligationCauseCode};
+use rustc_trait_selection::traits::{self, ObligationCauseCode, StatementAsExpression};
 
 use std::mem::replace;
 use std::slice;
@@ -758,13 +758,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expected_ty: Ty<'tcx>,
         err: &mut DiagnosticBuilder<'_>,
     ) {
-        if let Some(span_semi) = self.could_remove_semicolon(blk, expected_ty) {
-            err.span_suggestion(
-                span_semi,
-                "consider removing this semicolon",
-                String::new(),
-                Applicability::MachineApplicable,
-            );
+        if let Some((span_semi, boxed)) = self.could_remove_semicolon(blk, expected_ty) {
+            if let StatementAsExpression::NeedsBoxing = boxed {
+                err.span_suggestion_verbose(
+                    span_semi,
+                    "consider removing this semicolon and boxing the expression",
+                    String::new(),
+                    Applicability::HasPlaceholders,
+                );
+            } else {
+                err.span_suggestion_short(
+                    span_semi,
+                    "consider removing this semicolon",
+                    String::new(),
+                    Applicability::MachineApplicable,
+                );
+            }
         }
     }
 
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
index 9bad02c41b4..a8ad9f4fdf8 100644
--- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
@@ -3,7 +3,6 @@ use crate::astconv::AstConv;
 
 use rustc_ast::util::parser::ExprPrecedence;
 use rustc_span::{self, Span};
-use rustc_trait_selection::traits;
 
 use rustc_errors::{Applicability, DiagnosticBuilder};
 use rustc_hir as hir;
@@ -13,7 +12,6 @@ use rustc_hir::{ExprKind, ItemKind, Node};
 use rustc_infer::infer;
 use rustc_middle::ty::{self, Ty};
 use rustc_span::symbol::kw;
-use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
 
 use std::iter;
 
@@ -433,87 +431,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
-    /// A possible error is to forget to add `.await` when using futures:
-    ///
-    /// ```
-    /// async fn make_u32() -> u32 {
-    ///     22
-    /// }
-    ///
-    /// fn take_u32(x: u32) {}
-    ///
-    /// async fn foo() {
-    ///     let x = make_u32();
-    ///     take_u32(x);
-    /// }
-    /// ```
-    ///
-    /// This routine checks if the found type `T` implements `Future<Output=U>` where `U` is the
-    /// expected type. If this is the case, and we are inside of an async body, it suggests adding
-    /// `.await` to the tail of the expression.
-    pub(in super::super) fn suggest_missing_await(
-        &self,
-        err: &mut DiagnosticBuilder<'_>,
-        expr: &hir::Expr<'_>,
-        expected: Ty<'tcx>,
-        found: Ty<'tcx>,
-    ) {
-        debug!("suggest_missing_await: expr={:?} expected={:?}, found={:?}", expr, expected, found);
-        // `.await` is not permitted outside of `async` bodies, so don't bother to suggest if the
-        // body isn't `async`.
-        let item_id = self.tcx().hir().get_parent_node(self.body_id);
-        if let Some(body_id) = self.tcx().hir().maybe_body_owned_by(item_id) {
-            let body = self.tcx().hir().body(body_id);
-            if let Some(hir::GeneratorKind::Async(_)) = body.generator_kind {
-                let sp = expr.span;
-                // Check for `Future` implementations by constructing a predicate to
-                // prove: `<T as Future>::Output == U`
-                let future_trait = self.tcx.require_lang_item(LangItem::Future, Some(sp));
-                let item_def_id = self
-                    .tcx
-                    .associated_items(future_trait)
-                    .in_definition_order()
-                    .next()
-                    .unwrap()
-                    .def_id;
-                // `<T as Future>::Output`
-                let projection_ty = ty::ProjectionTy {
-                    // `T`
-                    substs: self
-                        .tcx
-                        .mk_substs_trait(found, self.fresh_substs_for_item(sp, item_def_id)),
-                    // `Future::Output`
-                    item_def_id,
-                };
-
-                let predicate = ty::PredicateAtom::Projection(ty::ProjectionPredicate {
-                    projection_ty,
-                    ty: expected,
-                })
-                .potentially_quantified(self.tcx, ty::PredicateKind::ForAll);
-                let obligation = traits::Obligation::new(self.misc(sp), self.param_env, predicate);
-
-                debug!("suggest_missing_await: trying obligation {:?}", obligation);
-
-                if self.infcx.predicate_may_hold(&obligation) {
-                    debug!("suggest_missing_await: obligation held: {:?}", obligation);
-                    if let Ok(code) = self.sess().source_map().span_to_snippet(sp) {
-                        err.span_suggestion(
-                            sp,
-                            "consider using `.await` here",
-                            format!("{}.await", code),
-                            Applicability::MaybeIncorrect,
-                        );
-                    } else {
-                        debug!("suggest_missing_await: no snippet for {:?}", sp);
-                    }
-                } else {
-                    debug!("suggest_missing_await: obligation did not hold: {:?}", obligation)
-                }
-            }
-        }
-    }
-
     pub(in super::super) fn suggest_missing_parentheses(
         &self,
         err: &mut DiagnosticBuilder<'_>,
diff --git a/src/test/ui/async-await/await-keyword/incorrect-syntax-suggestions.rs b/src/test/ui/async-await/await-keyword/incorrect-syntax-suggestions.rs
index 337487fc80b..554ac673d51 100644
--- a/src/test/ui/async-await/await-keyword/incorrect-syntax-suggestions.rs
+++ b/src/test/ui/async-await/await-keyword/incorrect-syntax-suggestions.rs
@@ -14,7 +14,6 @@ async fn foo2() -> Result<(), ()> {
 }
 async fn foo3() -> Result<(), ()> {
     let _ = await bar()?; //~ ERROR incorrect use of `await`
-    //~^ ERROR the `?` operator can only be applied to values that implement `Try`
     Ok(())
 }
 async fn foo21() -> Result<(), ()> {
@@ -60,9 +59,7 @@ fn foo10() -> Result<(), ()> {
     Ok(())
 }
 fn foo11() -> Result<(), ()> {
-    let _ = await bar()?; //~ ERROR `await` is only allowed inside `async` functions and blocks
-    //~^ ERROR incorrect use of `await`
-    //~| ERROR the `?` operator can only be applied to values that implement `Try`
+    let _ = await bar()?; //~ ERROR incorrect use of `await`
     Ok(())
 }
 fn foo12() -> Result<(), ()> {
diff --git a/src/test/ui/async-await/await-keyword/incorrect-syntax-suggestions.stderr b/src/test/ui/async-await/await-keyword/incorrect-syntax-suggestions.stderr
index 6a653fc060b..52615df6008 100644
--- a/src/test/ui/async-await/await-keyword/incorrect-syntax-suggestions.stderr
+++ b/src/test/ui/async-await/await-keyword/incorrect-syntax-suggestions.stderr
@@ -17,103 +17,103 @@ LL |     let _ = await bar()?;
    |             ^^^^^^^^^^^^ help: `await` is a postfix operation: `bar()?.await`
 
 error: incorrect use of `await`
-  --> $DIR/incorrect-syntax-suggestions.rs:21:13
+  --> $DIR/incorrect-syntax-suggestions.rs:20:13
    |
 LL |     let _ = await { bar() };
    |             ^^^^^^^^^^^^^^^ help: `await` is a postfix operation: `{ bar() }.await`
 
 error: incorrect use of `await`
-  --> $DIR/incorrect-syntax-suggestions.rs:25:13
+  --> $DIR/incorrect-syntax-suggestions.rs:24:13
    |
 LL |     let _ = await(bar());
    |             ^^^^^^^^^^^^ help: `await` is a postfix operation: `(bar()).await`
 
 error: incorrect use of `await`
-  --> $DIR/incorrect-syntax-suggestions.rs:29:13
+  --> $DIR/incorrect-syntax-suggestions.rs:28:13
    |
 LL |     let _ = await { bar() }?;
    |             ^^^^^^^^^^^^^^^ help: `await` is a postfix operation: `{ bar() }.await`
 
 error: incorrect use of `await`
-  --> $DIR/incorrect-syntax-suggestions.rs:33:14
+  --> $DIR/incorrect-syntax-suggestions.rs:32:14
    |
 LL |     let _ = (await bar())?;
    |              ^^^^^^^^^^^ help: `await` is a postfix operation: `bar().await`
 
 error: incorrect use of `await`
-  --> $DIR/incorrect-syntax-suggestions.rs:37:24
+  --> $DIR/incorrect-syntax-suggestions.rs:36:24
    |
 LL |     let _ = bar().await();
    |                        ^^ help: `await` is not a method call, remove the parentheses
 
 error: incorrect use of `await`
-  --> $DIR/incorrect-syntax-suggestions.rs:41:24
+  --> $DIR/incorrect-syntax-suggestions.rs:40:24
    |
 LL |     let _ = bar().await()?;
    |                        ^^ help: `await` is not a method call, remove the parentheses
 
 error: incorrect use of `await`
-  --> $DIR/incorrect-syntax-suggestions.rs:53:13
+  --> $DIR/incorrect-syntax-suggestions.rs:52:13
    |
 LL |     let _ = await bar();
    |             ^^^^^^^^^^^ help: `await` is a postfix operation: `bar().await`
 
 error: incorrect use of `await`
-  --> $DIR/incorrect-syntax-suggestions.rs:58:13
+  --> $DIR/incorrect-syntax-suggestions.rs:57:13
    |
 LL |     let _ = await? bar();
    |             ^^^^^^^^^^^^ help: `await` is a postfix operation: `bar().await?`
 
 error: incorrect use of `await`
-  --> $DIR/incorrect-syntax-suggestions.rs:63:13
+  --> $DIR/incorrect-syntax-suggestions.rs:62:13
    |
 LL |     let _ = await bar()?;
    |             ^^^^^^^^^^^^ help: `await` is a postfix operation: `bar()?.await`
 
 error: incorrect use of `await`
-  --> $DIR/incorrect-syntax-suggestions.rs:69:14
+  --> $DIR/incorrect-syntax-suggestions.rs:66:14
    |
 LL |     let _ = (await bar())?;
    |              ^^^^^^^^^^^ help: `await` is a postfix operation: `bar().await`
 
 error: incorrect use of `await`
-  --> $DIR/incorrect-syntax-suggestions.rs:74:24
+  --> $DIR/incorrect-syntax-suggestions.rs:71:24
    |
 LL |     let _ = bar().await();
    |                        ^^ help: `await` is not a method call, remove the parentheses
 
 error: incorrect use of `await`
-  --> $DIR/incorrect-syntax-suggestions.rs:79:24
+  --> $DIR/incorrect-syntax-suggestions.rs:76:24
    |
 LL |     let _ = bar().await()?;
    |                        ^^ help: `await` is not a method call, remove the parentheses
 
 error: incorrect use of `await`
-  --> $DIR/incorrect-syntax-suggestions.rs:107:13
+  --> $DIR/incorrect-syntax-suggestions.rs:104:13
    |
 LL |     let _ = await!(bar());
    |             ^^^^^^^^^^^^^ help: `await` is a postfix operation: `bar().await`
 
 error: incorrect use of `await`
-  --> $DIR/incorrect-syntax-suggestions.rs:111:13
+  --> $DIR/incorrect-syntax-suggestions.rs:108:13
    |
 LL |     let _ = await!(bar())?;
    |             ^^^^^^^^^^^^^ help: `await` is a postfix operation: `bar().await`
 
 error: incorrect use of `await`
-  --> $DIR/incorrect-syntax-suggestions.rs:116:17
+  --> $DIR/incorrect-syntax-suggestions.rs:113:17
    |
 LL |         let _ = await!(bar())?;
    |                 ^^^^^^^^^^^^^ help: `await` is a postfix operation: `bar().await`
 
 error: incorrect use of `await`
-  --> $DIR/incorrect-syntax-suggestions.rs:124:17
+  --> $DIR/incorrect-syntax-suggestions.rs:121:17
    |
 LL |         let _ = await!(bar())?;
    |                 ^^^^^^^^^^^^^ help: `await` is a postfix operation: `bar().await`
 
 error: expected expression, found `=>`
-  --> $DIR/incorrect-syntax-suggestions.rs:132:25
+  --> $DIR/incorrect-syntax-suggestions.rs:129:25
    |
 LL |     match await { await => () }
    |                   ----- ^^ expected expression
@@ -121,13 +121,13 @@ LL |     match await { await => () }
    |                   while parsing this incorrect await expression
 
 error: incorrect use of `await`
-  --> $DIR/incorrect-syntax-suggestions.rs:132:11
+  --> $DIR/incorrect-syntax-suggestions.rs:129:11
    |
 LL |     match await { await => () }
    |           ^^^^^^^^^^^^^^^^^^^^^ help: `await` is a postfix operation: `{ await => () }.await`
 
 error: expected one of `.`, `?`, `{`, or an operator, found `}`
-  --> $DIR/incorrect-syntax-suggestions.rs:135:1
+  --> $DIR/incorrect-syntax-suggestions.rs:132:1
    |
 LL |     match await { await => () }
    |     -----                      - expected one of `.`, `?`, `{`, or an operator
@@ -138,7 +138,7 @@ LL | }
    | ^ unexpected token
 
 error[E0728]: `await` is only allowed inside `async` functions and blocks
-  --> $DIR/incorrect-syntax-suggestions.rs:53:13
+  --> $DIR/incorrect-syntax-suggestions.rs:52:13
    |
 LL | fn foo9() -> Result<(), ()> {
    |    ---- this is not `async`
@@ -146,7 +146,7 @@ LL |     let _ = await bar();
    |             ^^^^^^^^^^^ only allowed inside `async` functions and blocks
 
 error[E0728]: `await` is only allowed inside `async` functions and blocks
-  --> $DIR/incorrect-syntax-suggestions.rs:58:13
+  --> $DIR/incorrect-syntax-suggestions.rs:57:13
    |
 LL | fn foo10() -> Result<(), ()> {
    |    ----- this is not `async`
@@ -154,15 +154,7 @@ LL |     let _ = await? bar();
    |             ^^^^^^^^^^^^ only allowed inside `async` functions and blocks
 
 error[E0728]: `await` is only allowed inside `async` functions and blocks
-  --> $DIR/incorrect-syntax-suggestions.rs:63:13
-   |
-LL | fn foo11() -> Result<(), ()> {
-   |    ----- this is not `async`
-LL |     let _ = await bar()?;
-   |             ^^^^^^^^^^^^ only allowed inside `async` functions and blocks
-
-error[E0728]: `await` is only allowed inside `async` functions and blocks
-  --> $DIR/incorrect-syntax-suggestions.rs:69:14
+  --> $DIR/incorrect-syntax-suggestions.rs:66:14
    |
 LL | fn foo12() -> Result<(), ()> {
    |    ----- this is not `async`
@@ -170,7 +162,7 @@ LL |     let _ = (await bar())?;
    |              ^^^^^^^^^^^ only allowed inside `async` functions and blocks
 
 error[E0728]: `await` is only allowed inside `async` functions and blocks
-  --> $DIR/incorrect-syntax-suggestions.rs:74:13
+  --> $DIR/incorrect-syntax-suggestions.rs:71:13
    |
 LL | fn foo13() -> Result<(), ()> {
    |    ----- this is not `async`
@@ -178,7 +170,7 @@ LL |     let _ = bar().await();
    |             ^^^^^^^^^^^ only allowed inside `async` functions and blocks
 
 error[E0728]: `await` is only allowed inside `async` functions and blocks
-  --> $DIR/incorrect-syntax-suggestions.rs:79:13
+  --> $DIR/incorrect-syntax-suggestions.rs:76:13
    |
 LL | fn foo14() -> Result<(), ()> {
    |    ----- this is not `async`
@@ -186,7 +178,7 @@ LL |     let _ = bar().await()?;
    |             ^^^^^^^^^^^ only allowed inside `async` functions and blocks
 
 error[E0728]: `await` is only allowed inside `async` functions and blocks
-  --> $DIR/incorrect-syntax-suggestions.rs:84:13
+  --> $DIR/incorrect-syntax-suggestions.rs:81:13
    |
 LL | fn foo15() -> Result<(), ()> {
    |    ----- this is not `async`
@@ -194,7 +186,7 @@ LL |     let _ = bar().await;
    |             ^^^^^^^^^^^ only allowed inside `async` functions and blocks
 
 error[E0728]: `await` is only allowed inside `async` functions and blocks
-  --> $DIR/incorrect-syntax-suggestions.rs:88:13
+  --> $DIR/incorrect-syntax-suggestions.rs:85:13
    |
 LL | fn foo16() -> Result<(), ()> {
    |    ----- this is not `async`
@@ -202,7 +194,7 @@ LL |     let _ = bar().await?;
    |             ^^^^^^^^^^^ only allowed inside `async` functions and blocks
 
 error[E0728]: `await` is only allowed inside `async` functions and blocks
-  --> $DIR/incorrect-syntax-suggestions.rs:93:17
+  --> $DIR/incorrect-syntax-suggestions.rs:90:17
    |
 LL |     fn foo() -> Result<(), ()> {
    |        --- this is not `async`
@@ -210,7 +202,7 @@ LL |         let _ = bar().await?;
    |                 ^^^^^^^^^^^ only allowed inside `async` functions and blocks
 
 error[E0728]: `await` is only allowed inside `async` functions and blocks
-  --> $DIR/incorrect-syntax-suggestions.rs:100:17
+  --> $DIR/incorrect-syntax-suggestions.rs:97:17
    |
 LL |     let foo = || {
    |               -- this is not `async`
@@ -218,7 +210,7 @@ LL |         let _ = bar().await?;
    |                 ^^^^^^^^^^^ only allowed inside `async` functions and blocks
 
 error[E0728]: `await` is only allowed inside `async` functions and blocks
-  --> $DIR/incorrect-syntax-suggestions.rs:116:17
+  --> $DIR/incorrect-syntax-suggestions.rs:113:17
    |
 LL |     fn foo() -> Result<(), ()> {
    |        --- this is not `async`
@@ -226,35 +218,13 @@ LL |         let _ = await!(bar())?;
    |                 ^^^^^^^^^^^^^ only allowed inside `async` functions and blocks
 
 error[E0728]: `await` is only allowed inside `async` functions and blocks
-  --> $DIR/incorrect-syntax-suggestions.rs:124:17
+  --> $DIR/incorrect-syntax-suggestions.rs:121:17
    |
 LL |     let foo = || {
    |               -- this is not `async`
 LL |         let _ = await!(bar())?;
    |                 ^^^^^^^^^^^^^ only allowed inside `async` functions and blocks
 
-error[E0277]: the `?` operator can only be applied to values that implement `Try`
-  --> $DIR/incorrect-syntax-suggestions.rs:16:19
-   |
-LL |     let _ = await bar()?;
-   |                   ^^^^^^
-   |                   |
-   |                   the `?` operator cannot be applied to type `impl Future`
-   |                   help: consider using `.await` here: `bar().await?`
-   |
-   = help: the trait `Try` is not implemented for `impl Future`
-   = note: required by `into_result`
-
-error[E0277]: the `?` operator can only be applied to values that implement `Try`
-  --> $DIR/incorrect-syntax-suggestions.rs:63:19
-   |
-LL |     let _ = await bar()?;
-   |                   ^^^^^^ the `?` operator cannot be applied to type `impl Future`
-   |
-   = help: the trait `Try` is not implemented for `impl Future`
-   = note: required by `into_result`
-
-error: aborting due to 36 previous errors
+error: aborting due to 33 previous errors
 
-Some errors have detailed explanations: E0277, E0728.
-For more information about an error, try `rustc --explain E0277`.
+For more information about this error, try `rustc --explain E0728`.
diff --git a/src/test/ui/async-await/dont-suggest-missing-await.stderr b/src/test/ui/async-await/dont-suggest-missing-await.stderr
index e70ed9badbd..14e72c2b1e7 100644
--- a/src/test/ui/async-await/dont-suggest-missing-await.stderr
+++ b/src/test/ui/async-await/dont-suggest-missing-await.stderr
@@ -9,6 +9,10 @@ LL |         take_u32(x)
    |
    = note:     expected type `u32`
            found opaque type `impl Future`
+help: consider `await`ing on the `Future`
+   |
+LL |         take_u32(x.await)
+   |                   ^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/async-await/issue-61076.stderr b/src/test/ui/async-await/issue-61076.stderr
index 88ea7251eaf..df54ac88ace 100644
--- a/src/test/ui/async-await/issue-61076.stderr
+++ b/src/test/ui/async-await/issue-61076.stderr
@@ -2,25 +2,27 @@ error[E0277]: the `?` operator can only be applied to values that implement `Try
   --> $DIR/issue-61076.rs:42:5
    |
 LL |     foo()?;
-   |     ^^^^^^
-   |     |
-   |     the `?` operator cannot be applied to type `impl Future`
-   |     help: consider using `.await` here: `foo().await?`
+   |     ^^^^^^ 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`
+   |
+LL |     foo().await?;
+   |          ^^^^^^
 
 error[E0277]: the `?` operator can only be applied to values that implement `Try`
   --> $DIR/issue-61076.rs:56:5
    |
 LL |     t?;
-   |     ^^
-   |     |
-   |     the `?` operator cannot be applied to type `T`
-   |     help: consider using `.await` here: `t.await?`
+   |     ^^ 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`
+   |
+LL |     t.await?;
+   |      ^^^^^^
 
 error[E0609]: no field `0` on type `impl Future`
   --> $DIR/issue-61076.rs:58:26
@@ -51,6 +53,10 @@ LL |         Tuple(_) => {}
    |
    = note: expected opaque type `impl Future`
                    found struct `Tuple`
+help: consider `await`ing on the `Future`
+   |
+LL |     match tuple().await {
+   |                  ^^^^^^
 
 error: aborting due to 6 previous errors
 
diff --git a/src/test/ui/async-await/suggest-missing-await-closure.fixed b/src/test/ui/async-await/suggest-missing-await-closure.fixed
index 37b30ffe680..febcd021842 100644
--- a/src/test/ui/async-await/suggest-missing-await-closure.fixed
+++ b/src/test/ui/async-await/suggest-missing-await-closure.fixed
@@ -15,8 +15,8 @@ async fn suggest_await_in_async_closure() {
         let x = make_u32();
         take_u32(x.await)
         //~^ ERROR mismatched types [E0308]
-        //~| HELP consider using `.await` here
-        //~| SUGGESTION x.await
+        //~| HELP consider `await`ing on the `Future`
+        //~| SUGGESTION .await
     };
 }
 
diff --git a/src/test/ui/async-await/suggest-missing-await-closure.rs b/src/test/ui/async-await/suggest-missing-await-closure.rs
index 18076a15161..faabf6ee3f1 100644
--- a/src/test/ui/async-await/suggest-missing-await-closure.rs
+++ b/src/test/ui/async-await/suggest-missing-await-closure.rs
@@ -15,8 +15,8 @@ async fn suggest_await_in_async_closure() {
         let x = make_u32();
         take_u32(x)
         //~^ ERROR mismatched types [E0308]
-        //~| HELP consider using `.await` here
-        //~| SUGGESTION x.await
+        //~| HELP consider `await`ing on the `Future`
+        //~| SUGGESTION .await
     };
 }
 
diff --git a/src/test/ui/async-await/suggest-missing-await-closure.stderr b/src/test/ui/async-await/suggest-missing-await-closure.stderr
index ed2c4cbfccc..2151057aa7f 100644
--- a/src/test/ui/async-await/suggest-missing-await-closure.stderr
+++ b/src/test/ui/async-await/suggest-missing-await-closure.stderr
@@ -5,13 +5,14 @@ LL | async fn make_u32() -> u32 {
    |                        --- the `Output` of this `async fn`'s found opaque type
 ...
 LL |         take_u32(x)
-   |                  ^
-   |                  |
-   |                  expected `u32`, found opaque type
-   |                  help: consider using `.await` here: `x.await`
+   |                  ^ expected `u32`, found opaque type
    |
    = note:     expected type `u32`
            found opaque type `impl Future`
+help: consider `await`ing on the `Future`
+   |
+LL |         take_u32(x.await)
+   |                   ^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/async-await/suggest-missing-await.fixed b/src/test/ui/async-await/suggest-missing-await.fixed
deleted file mode 100644
index 1ec59d90620..00000000000
--- a/src/test/ui/async-await/suggest-missing-await.fixed
+++ /dev/null
@@ -1,30 +0,0 @@
-// edition:2018
-// run-rustfix
-
-fn take_u32(_x: u32) {}
-
-async fn make_u32() -> u32 {
-    22
-}
-
-#[allow(unused)]
-async fn suggest_await_in_async_fn() {
-    let x = make_u32();
-    take_u32(x.await)
-    //~^ ERROR mismatched types [E0308]
-    //~| HELP consider using `.await` here
-    //~| SUGGESTION x.await
-}
-
-async fn dummy() {}
-
-#[allow(unused)]
-async fn suggest_await_in_async_fn_return() {
-    dummy().await;
-    //~^ ERROR mismatched types [E0308]
-    //~| HELP try adding a semicolon
-    //~| HELP consider using `.await` here
-    //~| SUGGESTION dummy().await
-}
-
-fn main() {}
diff --git a/src/test/ui/async-await/suggest-missing-await.rs b/src/test/ui/async-await/suggest-missing-await.rs
index 70cc1f1d5a2..d629054911d 100644
--- a/src/test/ui/async-await/suggest-missing-await.rs
+++ b/src/test/ui/async-await/suggest-missing-await.rs
@@ -1,5 +1,4 @@
 // edition:2018
-// run-rustfix
 
 fn take_u32(_x: u32) {}
 
@@ -12,8 +11,8 @@ async fn suggest_await_in_async_fn() {
     let x = make_u32();
     take_u32(x)
     //~^ ERROR mismatched types [E0308]
-    //~| HELP consider using `.await` here
-    //~| SUGGESTION x.await
+    //~| HELP consider `await`ing on the `Future`
+    //~| SUGGESTION .await
 }
 
 async fn dummy() {}
@@ -23,8 +22,8 @@ async fn suggest_await_in_async_fn_return() {
     dummy()
     //~^ ERROR mismatched types [E0308]
     //~| HELP try adding a semicolon
-    //~| HELP consider using `.await` here
-    //~| SUGGESTION dummy().await
+    //~| HELP consider `await`ing on the `Future`
+    //~| SUGGESTION .await
 }
 
 fn main() {}
diff --git a/src/test/ui/async-await/suggest-missing-await.stderr b/src/test/ui/async-await/suggest-missing-await.stderr
index c6355680e25..46615dae7e2 100644
--- a/src/test/ui/async-await/suggest-missing-await.stderr
+++ b/src/test/ui/async-await/suggest-missing-await.stderr
@@ -1,20 +1,21 @@
 error[E0308]: mismatched types
-  --> $DIR/suggest-missing-await.rs:13:14
+  --> $DIR/suggest-missing-await.rs:12:14
    |
 LL | async fn make_u32() -> u32 {
    |                        --- the `Output` of this `async fn`'s found opaque type
 ...
 LL |     take_u32(x)
-   |              ^
-   |              |
-   |              expected `u32`, found opaque type
-   |              help: consider using `.await` here: `x.await`
+   |              ^ expected `u32`, found opaque type
    |
    = note:     expected type `u32`
            found opaque type `impl Future`
+help: consider `await`ing on the `Future`
+   |
+LL |     take_u32(x.await)
+   |               ^^^^^^
 
 error[E0308]: mismatched types
-  --> $DIR/suggest-missing-await.rs:23:5
+  --> $DIR/suggest-missing-await.rs:22:5
    |
 LL | async fn dummy() {}
    |                  - the `Output` of this `async fn`'s found opaque type
@@ -24,14 +25,14 @@ LL |     dummy()
    |
    = note: expected unit type `()`
             found opaque type `impl Future`
+help: consider `await`ing on the `Future`
+   |
+LL |     dummy().await
+   |            ^^^^^^
 help: try adding a semicolon
    |
 LL |     dummy();
    |            ^
-help: consider using `.await` here
-   |
-LL |     dummy().await
-   |
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/suggestions/issue-72766.stderr b/src/test/ui/suggestions/issue-72766.stderr
index a1a5949b196..5c9c549fa07 100644
--- a/src/test/ui/suggestions/issue-72766.stderr
+++ b/src/test/ui/suggestions/issue-72766.stderr
@@ -2,13 +2,14 @@ error[E0277]: the `?` operator can only be applied to values that implement `Try
   --> $DIR/issue-72766.rs:14:5
    |
 LL |     SadGirl {}.call()?;
-   |     ^^^^^^^^^^^^^^^^^^
-   |     |
-   |     the `?` operator cannot be applied to type `impl Future`
-   |     help: consider using `.await` here: `SadGirl {}.call().await?`
+   |     ^^^^^^^^^^^^^^^^^^ 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`
+   |
+LL |     SadGirl {}.call().await?;
+   |                      ^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/suggestions/match-prev-arm-needing-semi.rs b/src/test/ui/suggestions/match-prev-arm-needing-semi.rs
new file mode 100644
index 00000000000..b8ac030b0bb
--- /dev/null
+++ b/src/test/ui/suggestions/match-prev-arm-needing-semi.rs
@@ -0,0 +1,57 @@
+// edition:2018
+
+fn dummy() -> i32 { 42 }
+
+fn extra_semicolon() {
+    let _ = match true { //~ NOTE `match` arms have incompatible types
+        true => {
+            dummy(); //~ NOTE this is found to be
+            //~^ HELP consider removing this semicolon
+        }
+        false => dummy(), //~ ERROR `match` arms have incompatible types
+        //~^ NOTE expected `()`, found `i32`
+    };
+}
+
+async fn async_dummy() {} //~ NOTE the `Output` of this `async fn`'s found opaque type
+async fn async_dummy2() {} //~ NOTE the `Output` of this `async fn`'s found opaque type
+//~^ NOTE the `Output` of this `async fn`'s found opaque type
+
+async fn async_extra_semicolon_same() {
+    let _ = match true { //~ NOTE `match` arms have incompatible types
+        true => {
+            async_dummy(); //~ NOTE this is found to be
+            //~^ HELP consider removing this semicolon
+        }
+        false => async_dummy(), //~ ERROR `match` arms have incompatible types
+        //~^ NOTE expected `()`, found opaque type
+        //~| NOTE expected type `()`
+        //~| HELP consider `await`ing on the `Future`
+    };
+}
+
+async fn async_extra_semicolon_different() {
+    let _ = match true { //~ NOTE `match` arms have incompatible types
+        true => {
+            async_dummy(); //~ NOTE this is found to be
+            //~^ HELP consider removing this semicolon
+        }
+        false => async_dummy2(), //~ ERROR `match` arms have incompatible types
+        //~^ NOTE expected `()`, found opaque type
+        //~| NOTE expected type `()`
+        //~| HELP consider `await`ing on the `Future`
+    };
+}
+
+async fn async_different_futures() {
+    let _ = match true { //~ NOTE `match` arms have incompatible types
+        true => async_dummy(), //~ NOTE this is found to be
+        //~| HELP consider `await`ing on both `Future`s
+        false => async_dummy2(), //~ ERROR `match` arms have incompatible types
+        //~^ NOTE expected opaque type, found a different opaque type
+        //~| NOTE expected type `impl Future`
+        //~| NOTE distinct uses of `impl Trait` result in different opaque types
+    };
+}
+
+fn main() {}
diff --git a/src/test/ui/suggestions/match-prev-arm-needing-semi.stderr b/src/test/ui/suggestions/match-prev-arm-needing-semi.stderr
new file mode 100644
index 00000000000..e9803a78f94
--- /dev/null
+++ b/src/test/ui/suggestions/match-prev-arm-needing-semi.stderr
@@ -0,0 +1,118 @@
+error[E0308]: `match` arms have incompatible types
+  --> $DIR/match-prev-arm-needing-semi.rs:26:18
+   |
+LL |   async fn async_dummy() {}
+   |                          - the `Output` of this `async fn`'s found opaque type
+...
+LL |       let _ = match true {
+   |  _____________-
+LL | |         true => {
+LL | |             async_dummy();
+   | |             -------------- this is found to be of type `()`
+LL | |
+LL | |         }
+LL | |         false => async_dummy(),
+   | |                  ^^^^^^^^^^^^^ expected `()`, found opaque type
+...  |
+LL | |
+LL | |     };
+   | |_____- `match` arms have incompatible types
+   |
+   = note:     expected type `()`
+           found opaque type `impl Future`
+help: consider `await`ing on the `Future`
+   |
+LL |         false => async_dummy().await,
+   |                               ^^^^^^
+help: consider removing this semicolon and boxing the expressions
+   |
+LL |             Box::new(async_dummy())
+LL |
+LL |         }
+LL |         false => Box::new(async_dummy()),
+   |
+
+error[E0308]: `match` arms have incompatible types
+  --> $DIR/match-prev-arm-needing-semi.rs:39:18
+   |
+LL |   async fn async_dummy2() {}
+   |                           - the `Output` of this `async fn`'s found opaque type
+...
+LL |       let _ = match true {
+   |  _____________-
+LL | |         true => {
+LL | |             async_dummy();
+   | |             -------------- this is found to be of type `()`
+LL | |
+LL | |         }
+LL | |         false => async_dummy2(),
+   | |                  ^^^^^^^^^^^^^^ expected `()`, found opaque type
+...  |
+LL | |
+LL | |     };
+   | |_____- `match` arms have incompatible types
+   |
+   = note:     expected type `()`
+           found opaque type `impl Future`
+help: consider `await`ing on the `Future`
+   |
+LL |         false => async_dummy2().await,
+   |                                ^^^^^^
+help: consider removing this semicolon and boxing the expressions
+   |
+LL |             Box::new(async_dummy())
+LL |
+LL |         }
+LL |         false => Box::new(async_dummy2()),
+   |
+
+error[E0308]: `match` arms have incompatible types
+  --> $DIR/match-prev-arm-needing-semi.rs:50:18
+   |
+LL |   async fn async_dummy2() {}
+   |                           - the `Output` of this `async fn`'s found opaque type
+...
+LL |       let _ = match true {
+   |  _____________-
+LL | |         true => async_dummy(),
+   | |                 ------------- this is found to be of type `impl Future`
+LL | |
+LL | |         false => async_dummy2(),
+   | |                  ^^^^^^^^^^^^^^ expected opaque type, found a different opaque type
+...  |
+LL | |
+LL | |     };
+   | |_____- `match` arms have incompatible types
+   |
+   = note:     expected type `impl Future` (opaque type at <$DIR/match-prev-arm-needing-semi.rs:16:24>)
+           found opaque type `impl Future` (opaque type at <$DIR/match-prev-arm-needing-semi.rs:17:25>)
+   = note: distinct uses of `impl Trait` result in different opaque types
+help: consider `await`ing on both `Future`s
+   |
+LL |         true => async_dummy().await,
+LL |
+LL |         false => async_dummy2().await,
+   |
+
+error[E0308]: `match` arms have incompatible types
+  --> $DIR/match-prev-arm-needing-semi.rs:11:18
+   |
+LL |       let _ = match true {
+   |  _____________-
+LL | |         true => {
+LL | |             dummy();
+   | |             --------
+   | |             |      |
+   | |             |      help: consider removing this semicolon
+   | |             this is found to be of type `()`
+LL | |
+LL | |         }
+LL | |         false => dummy(),
+   | |                  ^^^^^^^ expected `()`, found `i32`
+LL | |
+LL | |     };
+   | |_____- `match` arms have incompatible types
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/suggestions/opaque-type-error.stderr b/src/test/ui/suggestions/opaque-type-error.stderr
index a7c2b82942f..d74076cbc9b 100644
--- a/src/test/ui/suggestions/opaque-type-error.stderr
+++ b/src/test/ui/suggestions/opaque-type-error.stderr
@@ -16,6 +16,12 @@ LL | |     }.await
    = note:     expected type `impl Future` (opaque type at <$DIR/opaque-type-error.rs:8:19>)
            found opaque type `impl Future` (opaque type at <$DIR/opaque-type-error.rs:12:19>)
    = note: distinct uses of `impl Trait` result in different opaque types
+help: consider `await`ing on both `Future`s
+   |
+LL |         thing_one().await
+LL |     } else {
+LL |         thing_two().await
+   |
 
 error: aborting due to previous error