diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 9c58809123931..9fbb01216bbb6 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 5975c52ca46e8..b3b4c5a56fbd3 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>::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>, + call_ident: Option, + 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` 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 5ba58e7427581..f589e88f68e44 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>) -> Option<&[i32]> { arg //~ ERROR 5:5: 5:8: mismatched types [E0308] } + +pub fn bar(arg: Option<&Vec>) -> &[i32] { + arg.unwrap_or(&[]) //~ ERROR 9:19: 9:22: mismatched types [E0308] +} + +pub fn barzz<'a>(arg: Option<&'a Vec>, 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] { + 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 b7c7202113a16..ad423f86ef9e3 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>` 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`, found `&[_; 0]` + | | + | arguments to this method are incorrect + | + = note: expected reference `&Vec` + 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`, found `&[i32]` + | | + | arguments to this method are incorrect + | + = note: expected reference `&Vec` + 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`, found `&[_; 0]` + | | + | arguments to this method are incorrect + | + = note: expected reference `&Vec` + 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`.