about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2023-05-30 22:11:39 +0000
committerMichael Goulet <michael@errs.io>2023-06-08 16:30:05 +0000
commita9188226a87618f98ae039682e7616a50bcf6c23 (patch)
tree17b89f79f3d2d1d340cf5b38176477af2b7eb190
parentc92140e83831286c36882fa50cc9edc0ecbbc578 (diff)
downloadrust-a9188226a87618f98ae039682e7616a50bcf6c23.tar.gz
rust-a9188226a87618f98ae039682e7616a50bcf6c23.zip
Peel borrows before suggesting as_ref/as_deref
-rw-r--r--compiler/rustc_hir_typeck/src/errors.rs7
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs16
-rw-r--r--tests/ui/issues/issue-100605.stderr5
-rw-r--r--tests/ui/let-else/let-else-ref-bindings.stderr10
4 files changed, 28 insertions, 10 deletions
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<Span>,
     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<String>` 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<Vec<u8>>`
 help: try using `.as_deref()` to convert `&Option<Vec<u8>>` 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<Vec<u8>>`
 help: try using `.as_deref()` to convert `&Option<Vec<u8>>` 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