From be90f90810438eed6f0090acfb4d29a787a43c1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 21 Apr 2020 17:20:12 -0700 Subject: [PATCH] Point at the return type on `.into()` failure caused by `?` Fix #35946. --- .../traits/error_reporting/mod.rs | 38 ++++++++++++------- .../traits/error_reporting/suggestions.rs | 13 +++++++ src/test/ui/issues/issue-32709.stderr | 2 + src/test/ui/option-to-result.stderr | 6 +++ src/test/ui/try-on-option.stderr | 3 ++ 5 files changed, 48 insertions(+), 14 deletions(-) diff --git a/src/librustc_trait_selection/traits/error_reporting/mod.rs b/src/librustc_trait_selection/traits/error_reporting/mod.rs index 8a9017960fb23..094168d5d2e40 100644 --- a/src/librustc_trait_selection/traits/error_reporting/mod.rs +++ b/src/librustc_trait_selection/traits/error_reporting/mod.rs @@ -317,20 +317,30 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { .starts_with("std::convert::From` into a `Result` using `Option::ok_or` or `Option::ok_or_else`", - ".ok_or_else(|| /* error value */)".to_string(), - Applicability::HasPlaceholders, - ); - } else if is_try && is_from && should_convert_result_to_option { - err.span_suggestion_verbose( - span.shrink_to_lo(), - "consider converting the `Result` into an `Option` using `Result::ok`", - ".ok()".to_string(), - Applicability::MachineApplicable, - ); + if is_try && is_from { + if should_convert_option_to_result { + err.span_suggestion_verbose( + span.shrink_to_lo(), + "consider converting the `Option` into a `Result` \ + using `Option::ok_or` or `Option::ok_or_else`", + ".ok_or_else(|| /* error value */)".to_string(), + Applicability::HasPlaceholders, + ); + } else if should_convert_result_to_option { + err.span_suggestion_verbose( + span.shrink_to_lo(), + "consider converting the `Result` into an `Option` \ + using `Result::ok`", + ".ok()".to_string(), + Applicability::MachineApplicable, + ); + } + if let Some(ret_span) = self.return_type_span(obligation) { + err.span_label( + ret_span, + &format!("expected `{}` because of this", trait_ref.self_ty()), + ); + } } let explanation = diff --git a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs b/src/librustc_trait_selection/traits/error_reporting/suggestions.rs index 2b19699d6ec75..ce34d5868a8ae 100644 --- a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs +++ b/src/librustc_trait_selection/traits/error_reporting/suggestions.rs @@ -84,6 +84,8 @@ pub trait InferCtxtExt<'tcx> { trait_ref: &ty::Binder>, ); + fn return_type_span(&self, obligation: &PredicateObligation<'tcx>) -> Option; + fn suggest_impl_trait( &self, err: &mut DiagnosticBuilder<'tcx>, @@ -760,6 +762,17 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { } } + fn return_type_span(&self, obligation: &PredicateObligation<'tcx>) -> Option { + let hir = self.tcx.hir(); + let parent_node = hir.get_parent_node(obligation.cause.body_id); + let sig = match hir.find(parent_node) { + Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, ..), .. })) => sig, + _ => return None, + }; + + if let hir::FnRetTy::Return(ret_ty) = sig.decl.output { Some(ret_ty.span) } else { None } + } + /// If all conditions are met to identify a returned `dyn Trait`, suggest using `impl Trait` if /// applicable and signal that the error has been expanded appropriately and needs to be /// emitted. diff --git a/src/test/ui/issues/issue-32709.stderr b/src/test/ui/issues/issue-32709.stderr index 04b8c3aa35396..af272633f210d 100644 --- a/src/test/ui/issues/issue-32709.stderr +++ b/src/test/ui/issues/issue-32709.stderr @@ -1,6 +1,8 @@ error[E0277]: `?` couldn't convert the error to `()` --> $DIR/issue-32709.rs:4:11 | +LL | fn a() -> Result { + | --------------- expected `()` because of this LL | Err(5)?; | ^ the trait `std::convert::From<{integer}>` is not implemented for `()` | diff --git a/src/test/ui/option-to-result.stderr b/src/test/ui/option-to-result.stderr index f673ef7fc1e69..5fa06778389d9 100644 --- a/src/test/ui/option-to-result.stderr +++ b/src/test/ui/option-to-result.stderr @@ -1,6 +1,9 @@ error[E0277]: `?` couldn't convert the error to `()` --> $DIR/option-to-result.rs:5:6 | +LL | fn test_result() -> Result<(),()> { + | ------------- expected `()` because of this +LL | let a:Option<()> = Some(()); LL | a?; | ^ the trait `std::convert::From` is not implemented for `()` | @@ -14,6 +17,9 @@ LL | a.ok_or_else(|| /* error value */)?; error[E0277]: `?` couldn't convert the error to `std::option::NoneError` --> $DIR/option-to-result.rs:11:6 | +LL | fn test_option() -> Option{ + | ----------- expected `std::option::NoneError` because of this +LL | let a:Result = Ok(5); LL | a?; | ^ the trait `std::convert::From` is not implemented for `std::option::NoneError` | diff --git a/src/test/ui/try-on-option.stderr b/src/test/ui/try-on-option.stderr index 7a4bb75967b1f..33ca58bf7feb1 100644 --- a/src/test/ui/try-on-option.stderr +++ b/src/test/ui/try-on-option.stderr @@ -1,6 +1,9 @@ error[E0277]: `?` couldn't convert the error to `()` --> $DIR/try-on-option.rs:7:6 | +LL | fn foo() -> Result { + | --------------- expected `()` because of this +LL | let x: Option = None; LL | x?; | ^ the trait `std::convert::From` is not implemented for `()` |