diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 20116fde661..5161a366ae7 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -329,15 +329,16 @@ pub struct CtorIsPrivate { } #[derive(Subdiagnostic)] -#[suggestion( +#[multipart_suggestion( hir_typeck_convert_using_method, - code = "{sugg}", applicability = "machine-applicable", style = "verbose" )] pub struct SuggestConvertViaMethod<'tcx> { - #[primary_span] + #[suggestion_part(code = "{sugg}")] pub span: Span, + #[suggestion_part(code = "")] + pub borrow_removal_span: Option, pub sugg: &'static str, pub expected: Ty<'tcx>, pub found: Ty<'tcx>, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 21a52d3eccc..3a4fe334f88 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -413,7 +413,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.deconstruct_option_or_result(found, expected) && let ty::Ref(_, peeled, hir::Mutability::Not) = *expected_ty_inner.kind() { - // Check that given `Result<_, E>`, our expected ty is `Result<_, &E>` + // Suggest removing any stray borrows (unless there's macro shenanigans involved). + let inner_expr = expr.peel_borrows(); + if !inner_expr.span.eq_ctxt(expr.span) { + return false; + } + let borrow_removal_span = if inner_expr.hir_id == expr.hir_id { + None + } else { + Some(expr.span.shrink_to_lo().until(inner_expr.span)) + }; + // Given `Result<_, E>`, check our expected ty is `Result<_, &E>` for + // `as_ref` and `as_deref` compatibility. let error_tys_equate_as_ref = error_tys.map_or(true, |(found, expected)| { self.can_eq(self.param_env, self.tcx.mk_imm_ref(self.tcx.lifetimes.re_erased, found), expected) }); @@ -425,6 +436,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { sugg: ".as_ref()", expected, found, + borrow_removal_span, }); return true; } else if let Some((deref_ty, _)) = @@ -437,11 +449,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { sugg: ".as_deref()", expected, found, + borrow_removal_span, }); return true; } else if let ty::Adt(adt, _) = found_ty_inner.peel_refs().kind() && Some(adt.did()) == self.tcx.lang_items().string() && peeled.is_str() + // `Result::map`, conversely, does not take ref of the error type. && error_tys.map_or(true, |(found, expected)| { self.can_eq(self.param_env, found, expected) }) diff --git a/tests/ui/issues/issue-100605.stderr b/tests/ui/issues/issue-100605.stderr index e4fc5cb367c..6f11f44755a 100644 --- a/tests/ui/issues/issue-100605.stderr +++ b/tests/ui/issues/issue-100605.stderr @@ -36,8 +36,9 @@ LL | fn takes_option(_arg: Option<&String>) {} | ^^^^^^^^^^^^ --------------------- help: try using `.as_ref()` to convert `&Option` to `Option<&String>` | -LL | takes_option(&res.as_ref()); - | +++++++++ +LL - takes_option(&res); +LL + takes_option(res.as_ref()); + | error: aborting due to 2 previous errors diff --git a/tests/ui/let-else/let-else-ref-bindings.stderr b/tests/ui/let-else/let-else-ref-bindings.stderr index 7093b5fa9af..0886d7f1770 100644 --- a/tests/ui/let-else/let-else-ref-bindings.stderr +++ b/tests/ui/let-else/let-else-ref-bindings.stderr @@ -21,8 +21,9 @@ LL | let Some(ref a): Option<&[u8]> = &some else { return }; found reference `&Option>` help: try using `.as_deref()` to convert `&Option>` to `Option<&[u8]>` | -LL | let Some(ref a): Option<&[u8]> = &some.as_deref() else { return }; - | +++++++++++ +LL - let Some(ref a): Option<&[u8]> = &some else { return }; +LL + let Some(ref a): Option<&[u8]> = some.as_deref() else { return }; + | error[E0308]: mismatched types --> $DIR/let-else-ref-bindings.rs:24:34 @@ -51,8 +52,9 @@ LL | let Some(a): Option<&[u8]> = &some else { return }; found reference `&Option>` help: try using `.as_deref()` to convert `&Option>` to `Option<&[u8]>` | -LL | let Some(a): Option<&[u8]> = &some.as_deref() else { return }; - | +++++++++++ +LL - let Some(a): Option<&[u8]> = &some else { return }; +LL + let Some(a): Option<&[u8]> = some.as_deref() else { return }; + | error[E0308]: mismatched types --> $DIR/let-else-ref-bindings.rs:44:46