Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suggest using map_or when Option<&T>::unwrap_or where T: Deref fails #127629

Merged
merged 5 commits into from
Jul 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
68 changes: 68 additions & 0 deletions compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
12 changes: 12 additions & 0 deletions tests/ui/mismatched_types/transforming-option-ref-issue-127545.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
}
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Loading