diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 5f897c74482d6..5975c52ca46e8 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -466,21 +466,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { borrow_removal_span, }); return true; - } else if let Some((deref_ty, _)) = - self.autoderef(expr.span, found_ty_inner).silence_errors().nth(1) - && self.can_eq(self.param_env, deref_ty, peeled) - && error_tys_equate_as_ref - { - let sugg = prefix_wrap(".as_deref()"); - err.subdiagnostic(errors::SuggestConvertViaMethod { - span: expr.span.shrink_to_hi(), - sugg, - expected, - found, - borrow_removal_span, - }); - return true; - } else if let ty::Adt(adt, _) = found_ty_inner.peel_refs().kind() + } else if let ty::Ref(_, peeled_found_ty, _) = found_ty_inner.kind() + && let ty::Adt(adt, _) = peeled_found_ty.peel_refs().kind() && self.tcx.is_lang_item(adt.did(), LangItem::String) && peeled.is_str() // `Result::map`, conversely, does not take ref of the error type. @@ -496,12 +483,47 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MachineApplicable, ); return true; + } else { + if !error_tys_equate_as_ref { + return false; + } + let mut steps = self.autoderef(expr.span, found_ty_inner).silence_errors(); + if let Some((deref_ty, _)) = steps.nth(1) + && self.can_eq(self.param_env, deref_ty, peeled) + { + let sugg = prefix_wrap(".as_deref()"); + err.subdiagnostic(errors::SuggestConvertViaMethod { + span: expr.span.shrink_to_hi(), + sugg, + expected, + found, + borrow_removal_span, + }); + return true; + } + for (deref_ty, n_step) in steps { + if self.can_eq(self.param_env, deref_ty, peeled) { + let explicit_deref = "*".repeat(n_step); + let sugg = prefix_wrap(&format!(".map(|v| &{explicit_deref}v)")); + err.subdiagnostic(errors::SuggestConvertViaMethod { + span: expr.span.shrink_to_hi(), + sugg, + expected, + found, + borrow_removal_span, + }); + return true; + } + } } } false } + /// If `ty` is `Option`, returns `T, T, None`. + /// If `ty` is `Result`, returns `T, T, Some(E, E)`. + /// Otherwise, returns `None`. fn deconstruct_option_or_result( &self, found_ty: Ty<'tcx>, diff --git a/tests/ui/mismatched_types/transforming-option-ref-issue-127545.rs b/tests/ui/mismatched_types/transforming-option-ref-issue-127545.rs new file mode 100644 index 0000000000000..5ba58e7427581 --- /dev/null +++ b/tests/ui/mismatched_types/transforming-option-ref-issue-127545.rs @@ -0,0 +1,6 @@ +// Regression test for . +#![crate_type = "lib"] + +pub fn foo(arg: Option<&Vec>) -> Option<&[i32]> { + arg //~ ERROR 5:5: 5:8: mismatched types [E0308] +} diff --git a/tests/ui/mismatched_types/transforming-option-ref-issue-127545.stderr b/tests/ui/mismatched_types/transforming-option-ref-issue-127545.stderr new file mode 100644 index 0000000000000..b7c7202113a16 --- /dev/null +++ b/tests/ui/mismatched_types/transforming-option-ref-issue-127545.stderr @@ -0,0 +1,18 @@ +error[E0308]: mismatched types + --> $DIR/transforming-option-ref-issue-127545.rs:5:5 + | +LL | pub fn foo(arg: Option<&Vec>) -> Option<&[i32]> { + | -------------- expected `Option<&[i32]>` because of return type +LL | arg + | ^^^ expected `Option<&[i32]>`, found `Option<&Vec>` + | + = note: expected enum `Option<&[i32]>` + found enum `Option<&Vec>` +help: try using `.map(|v| &**v)` to convert `Option<&Vec>` to `Option<&[i32]>` + | +LL | arg.map(|v| &**v) + | ++++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`.