diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 3a0ec6327c186..f7e4ace8fc5fc 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> { + 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` 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 bbc46b8d60835..4deb7225dcb61 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 { + 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, + pub semi_span: Option<(Span, StatementAsExpression)>, pub source: hir::MatchSource, pub prior_arms: Vec, pub last_ty: Ty<'tcx>, @@ -357,7 +370,7 @@ pub struct IfExpressionCause { pub then: Span, pub else_sp: Span, pub outer: Option, - pub semicolon: Option, + pub semicolon: Option<(Span, StatementAsExpression)>, pub opt_suggest_box_span: Option, } diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index 235f8749cf917..5ec0ec0c56ad6 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 Trait 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 1ea01d95a134e..39e1256a57835 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 efa9bd633ba8c..fa837e04db35e 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 398e013e62fb5..e8eea65137ff7 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>, + 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>, - ) -> (Span, Option) { + ) -> (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 b8143787a2ddf..241803fab1e68 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 017b0abd1d607..f87e6b607d46e 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 { + ) -> 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 fd2f5eb5018d4..a820661d8432a 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 9bad02c41b4b1..a8ad9f4fdf8af 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` 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: `::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; - // `::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 337487fc80b0e..554ac673d5155 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 6a653fc060b1d..52615df6008ff 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 e70ed9badbd33..14e72c2b1e7e2 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 88ea7251eaf1f..df54ac88acee1 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 37b30ffe6800f..febcd02184261 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 18076a1516171..faabf6ee3f16f 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 ed2c4cbfccc98..2151057aa7fc0 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 1ec59d906206e..0000000000000 --- 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 70cc1f1d5a2c6..d629054911dac 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 c6355680e253b..46615dae7e2ba 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 a1a5949b19660..5c9c549fa0779 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 0000000000000..b8ac030b0bbbe --- /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 0000000000000..e9803a78f94b3 --- /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 a7c2b82942f05..d74076cbc9b8e 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