Peel borrows before suggesting as_ref/as_deref

This commit is contained in:
Michael Goulet 2023-05-30 22:11:39 +00:00
parent c92140e838
commit a9188226a8
4 changed files with 28 additions and 10 deletions

View File

@ -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>,

View File

@ -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)
})

View File

@ -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

View File

@ -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