about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-07-15 23:36:22 +0000
committerbors <bors@rust-lang.org>2024-07-15 23:36:22 +0000
commitcae4a84146c88cf917fa102c97ad9427591f6040 (patch)
treec0d1353c8e9b4c83e27f60edecff85f77a476ba1
parent24d2ac0b56fcbde13d827745f66e73efb1e17156 (diff)
parentbdc9df247841e259c24e95fd81754fb61e332c65 (diff)
downloadrust-cae4a84146c88cf917fa102c97ad9427591f6040.tar.gz
rust-cae4a84146c88cf917fa102c97ad9427591f6040.zip
Auto merge of #127629 - tesuji:suggest-option-ref-unwrap_or, r=estebank
Suggest using `map_or` when `Option<&T>::unwrap_or where T: Deref` fails

Fix #127545

Split from https://github.com/rust-lang/rust/pull/127596#pullrequestreview-2171898588
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs11
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs68
-rw-r--r--tests/ui/mismatched_types/transforming-option-ref-issue-127545.rs12
-rw-r--r--tests/ui/mismatched_types/transforming-option-ref-issue-127545.stderr74
4 files changed, 164 insertions, 1 deletions
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index 9c588091239..9fbb01216bb 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -948,6 +948,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 &mut err,
             );
 
+            self.suggest_deref_unwrap_or(
+                &mut err,
+                error_span,
+                callee_ty,
+                call_ident,
+                expected_ty,
+                provided_ty,
+                provided_args[*provided_idx],
+                is_method,
+            );
+
             // Call out where the function is defined
             self.label_fn_like(
                 &mut err,
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index 5975c52ca46..b3b4c5a56fb 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -1429,6 +1429,74 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         true
     }
 
+    // Suggest to change `Option<&Vec<T>>::unwrap_or(&[])` to `Option::map_or(&[], |v| v)`.
+    #[instrument(level = "trace", skip(self, err, provided_expr))]
+    pub(crate) fn suggest_deref_unwrap_or(
+        &self,
+        err: &mut Diag<'_>,
+        error_span: Span,
+        callee_ty: Option<Ty<'tcx>>,
+        call_ident: Option<Ident>,
+        expected_ty: Ty<'tcx>,
+        provided_ty: Ty<'tcx>,
+        provided_expr: &Expr<'tcx>,
+        is_method: bool,
+    ) {
+        if !is_method {
+            return;
+        }
+        let Some(callee_ty) = callee_ty else {
+            return;
+        };
+        let ty::Adt(callee_adt, _) = callee_ty.peel_refs().kind() else {
+            return;
+        };
+        let adt_name = if self.tcx.is_diagnostic_item(sym::Option, callee_adt.did()) {
+            "Option"
+        } else if self.tcx.is_diagnostic_item(sym::Result, callee_adt.did()) {
+            "Result"
+        } else {
+            return;
+        };
+
+        let Some(call_ident) = call_ident else {
+            return;
+        };
+        if call_ident.name != sym::unwrap_or {
+            return;
+        }
+
+        let ty::Ref(_, peeled, _mutability) = provided_ty.kind() else {
+            return;
+        };
+
+        // NOTE: Can we reuse `suggest_deref_or_ref`?
+
+        // Create an dummy type `&[_]` so that both &[] and `&Vec<T>` can coerce to it.
+        let dummy_ty = if let ty::Array(elem_ty, size) = peeled.kind()
+            && let ty::Infer(_) = elem_ty.kind()
+            && size.try_eval_target_usize(self.tcx, self.param_env) == Some(0)
+        {
+            let slice = Ty::new_slice(self.tcx, *elem_ty);
+            Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, slice)
+        } else {
+            provided_ty
+        };
+
+        if !self.can_coerce(expected_ty, dummy_ty) {
+            return;
+        }
+        let msg = format!("use `{adt_name}::map_or` to deref inner value of `{adt_name}`");
+        err.multipart_suggestion_verbose(
+            msg,
+            vec![
+                (call_ident.span, "map_or".to_owned()),
+                (provided_expr.span.shrink_to_hi(), ", |v| v".to_owned()),
+            ],
+            Applicability::MachineApplicable,
+        );
+    }
+
     /// Suggest wrapping the block in square brackets instead of curly braces
     /// in case the block was mistaken array syntax, e.g. `{ 1 }` -> `[ 1 ]`.
     pub(crate) fn suggest_block_to_brackets(
diff --git a/tests/ui/mismatched_types/transforming-option-ref-issue-127545.rs b/tests/ui/mismatched_types/transforming-option-ref-issue-127545.rs
index 5ba58e74275..f589e88f68e 100644
--- a/tests/ui/mismatched_types/transforming-option-ref-issue-127545.rs
+++ b/tests/ui/mismatched_types/transforming-option-ref-issue-127545.rs
@@ -4,3 +4,15 @@
 pub fn foo(arg: Option<&Vec<i32>>) -> Option<&[i32]> {
     arg //~ ERROR 5:5: 5:8: mismatched types [E0308]
 }
+
+pub fn bar(arg: Option<&Vec<i32>>) -> &[i32] {
+    arg.unwrap_or(&[]) //~ ERROR 9:19: 9:22: mismatched types [E0308]
+}
+
+pub fn barzz<'a>(arg: Option<&'a Vec<i32>>, v: &'a [i32]) -> &'a [i32] {
+    arg.unwrap_or(v) //~ ERROR 13:19: 13:20: mismatched types [E0308]
+}
+
+pub fn convert_result(arg: Result<&Vec<i32>, ()>) -> &[i32] {
+    arg.unwrap_or(&[]) //~ ERROR 17:19: 17:22: 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
index b7c7202113a..ad423f86ef9 100644
--- a/tests/ui/mismatched_types/transforming-option-ref-issue-127545.stderr
+++ b/tests/ui/mismatched_types/transforming-option-ref-issue-127545.stderr
@@ -13,6 +13,78 @@ help: try using `.map(|v| &**v)` to convert `Option<&Vec<i32>>` to `Option<&[i32
 LL |     arg.map(|v| &**v)
    |        ++++++++++++++
 
-error: aborting due to 1 previous error
+error[E0308]: mismatched types
+  --> $DIR/transforming-option-ref-issue-127545.rs:9:19
+   |
+LL |     arg.unwrap_or(&[])
+   |         --------- ^^^ expected `&Vec<i32>`, found `&[_; 0]`
+   |         |
+   |         arguments to this method are incorrect
+   |
+   = note: expected reference `&Vec<i32>`
+              found reference `&[_; 0]`
+help: the return type of this call is `&[_; 0]` due to the type of the argument passed
+  --> $DIR/transforming-option-ref-issue-127545.rs:9:5
+   |
+LL |     arg.unwrap_or(&[])
+   |     ^^^^^^^^^^^^^^---^
+   |                   |
+   |                   this argument influences the return type of `unwrap_or`
+note: method defined here
+  --> $SRC_DIR/core/src/option.rs:LL:COL
+help: use `Option::map_or` to deref inner value of `Option`
+   |
+LL |     arg.map_or(&[], |v| v)
+   |         ~~~~~~    +++++++
+
+error[E0308]: mismatched types
+  --> $DIR/transforming-option-ref-issue-127545.rs:13:19
+   |
+LL |     arg.unwrap_or(v)
+   |         --------- ^ expected `&Vec<i32>`, found `&[i32]`
+   |         |
+   |         arguments to this method are incorrect
+   |
+   = note: expected reference `&Vec<i32>`
+              found reference `&'a [i32]`
+help: the return type of this call is `&'a [i32]` due to the type of the argument passed
+  --> $DIR/transforming-option-ref-issue-127545.rs:13:5
+   |
+LL |     arg.unwrap_or(v)
+   |     ^^^^^^^^^^^^^^-^
+   |                   |
+   |                   this argument influences the return type of `unwrap_or`
+note: method defined here
+  --> $SRC_DIR/core/src/option.rs:LL:COL
+help: use `Option::map_or` to deref inner value of `Option`
+   |
+LL |     arg.map_or(v, |v| v)
+   |         ~~~~~~  +++++++
+
+error[E0308]: mismatched types
+  --> $DIR/transforming-option-ref-issue-127545.rs:17:19
+   |
+LL |     arg.unwrap_or(&[])
+   |         --------- ^^^ expected `&Vec<i32>`, found `&[_; 0]`
+   |         |
+   |         arguments to this method are incorrect
+   |
+   = note: expected reference `&Vec<i32>`
+              found reference `&[_; 0]`
+help: the return type of this call is `&[_; 0]` due to the type of the argument passed
+  --> $DIR/transforming-option-ref-issue-127545.rs:17:5
+   |
+LL |     arg.unwrap_or(&[])
+   |     ^^^^^^^^^^^^^^---^
+   |                   |
+   |                   this argument influences the return type of `unwrap_or`
+note: method defined here
+  --> $SRC_DIR/core/src/result.rs:LL:COL
+help: use `Result::map_or` to deref inner value of `Result`
+   |
+LL |     arg.map_or(&[], |v| v)
+   |         ~~~~~~    +++++++
+
+error: aborting due to 4 previous errors
 
 For more information about this error, try `rustc --explain E0308`.