Skip to content

Commit

Permalink
Suggest .as_ref() on borrow error involving Option/Result
Browse files Browse the repository at this point in the history
When encountering a E0382 borrow error involving an `Option` or `Result`
provide a suggestion to use `.as_ref()` on the prior move location to
avoid the move.

Fix #84165.
  • Loading branch information
estebank committed Apr 20, 2021
1 parent 010c236 commit 2763a05
Show file tree
Hide file tree
Showing 7 changed files with 134 additions and 62 deletions.
Expand Up @@ -197,7 +197,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
);
}
}
FnSelfUseKind::Normal { self_arg, implicit_into_iter } => {
FnSelfUseKind::Normal {
self_arg,
implicit_into_iter,
is_option_or_result,
} => {
if implicit_into_iter {
err.span_label(
fn_call_span,
Expand All @@ -215,6 +219,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
),
);
}
if is_option_or_result {
err.span_suggestion_verbose(
fn_call_span.shrink_to_lo(),
"consider calling `.as_ref()` to borrow the type's contents",
"as_ref().".to_string(),
Applicability::MachineApplicable,
);
}
// Avoid pointing to the same function in multiple different
// error messages.
if span != DUMMY_SP && self.fn_self_span_reported.insert(self_arg.span)
Expand Down
20 changes: 18 additions & 2 deletions compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs
Expand Up @@ -573,7 +573,13 @@ pub(super) enum UseSpans<'tcx> {
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub(super) enum FnSelfUseKind<'tcx> {
/// A normal method call of the form `receiver.foo(a, b, c)`
Normal { self_arg: Ident, implicit_into_iter: bool },
Normal {
self_arg: Ident,
implicit_into_iter: bool,
/// Whether the self type of the method call has an `.as_ref()` method.
/// Used for better diagnostics.
is_option_or_result: bool,
},
/// A call to `FnOnce::call_once`, desugared from `my_closure(a, b, c)`
FnOnceCall,
/// A call to an operator trait, desuraged from operator syntax (e.g. `a << b`)
Expand Down Expand Up @@ -900,7 +906,17 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
fn_call_span.desugaring_kind(),
Some(DesugaringKind::ForLoop(ForLoopLoc::IntoIter))
);
FnSelfUseKind::Normal { self_arg, implicit_into_iter }
let parent_self_ty = parent
.filter(|did| tcx.def_kind(*did) == rustc_hir::def::DefKind::Impl)
.and_then(|did| match tcx.type_of(did).kind() {
ty::Adt(def, ..) => Some(def.did),
_ => None,
});
let is_option_or_result = parent_self_ty.map_or(false, |def_id| {
tcx.is_diagnostic_item(sym::option_type, def_id)
|| tcx.is_diagnostic_item(sym::result_type, def_id)
});
FnSelfUseKind::Normal { self_arg, implicit_into_iter, is_option_or_result }
});

return FnSelfUse {
Expand Down
13 changes: 13 additions & 0 deletions src/test/ui/suggestions/as-ref-2.fixed
@@ -0,0 +1,13 @@
// run-rustfix

struct Struct;

fn bar(_: &Struct) -> Struct {
Struct
}

fn main() {
let foo = Some(Struct);
let _x: Option<Struct> = foo.as_ref().map(|s| bar(&s));
let _y = foo; //~ERROR use of moved value: `foo`
}
13 changes: 13 additions & 0 deletions src/test/ui/suggestions/as-ref-2.rs
@@ -0,0 +1,13 @@
// run-rustfix

struct Struct;

fn bar(_: &Struct) -> Struct {
Struct
}

fn main() {
let foo = Some(Struct);
let _x: Option<Struct> = foo.map(|s| bar(&s));
let _y = foo; //~ERROR use of moved value: `foo`
}
23 changes: 23 additions & 0 deletions src/test/ui/suggestions/as-ref-2.stderr
@@ -0,0 +1,23 @@
error[E0382]: use of moved value: `foo`
--> $DIR/as-ref-2.rs:12:14
|
LL | let foo = Some(Struct);
| --- move occurs because `foo` has type `Option<Struct>`, which does not implement the `Copy` trait
LL | let _x: Option<Struct> = foo.map(|s| bar(&s));
| ---------------- `foo` moved due to this method call
LL | let _y = foo;
| ^^^ value used here after move
|
note: this function takes ownership of the receiver `self`, which moves `foo`
--> $SRC_DIR/core/src/option.rs:LL:COL
|
LL | pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Option<U> {
| ^^^^
help: consider calling `.as_ref()` to borrow the type's contents
|
LL | let _x: Option<Struct> = foo.as_ref().map(|s| bar(&s));
| ^^^^^^^^^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0382`.
35 changes: 15 additions & 20 deletions src/test/ui/suggestions/as-ref.rs
@@ -1,25 +1,20 @@
struct Foo;

fn takes_ref(_: &Foo) {}

fn main() {
let ref opt = Some(Foo);
opt.map(|arg| takes_ref(arg));
//~^ ERROR mismatched types [E0308]
opt.and_then(|arg| Some(takes_ref(arg)));
//~^ ERROR mismatched types [E0308]
let ref opt: Result<_, ()> = Ok(Foo);
opt.map(|arg| takes_ref(arg));
//~^ ERROR mismatched types [E0308]
opt.and_then(|arg| Ok(takes_ref(arg)));
//~^ ERROR mismatched types [E0308]
let x: &Option<usize> = &Some(3);
let y: Option<&usize> = x;
//~^ ERROR mismatched types [E0308]
let x: &Result<usize, usize> = &Ok(3);
let y: Result<&usize, &usize> = x;
//~^ ERROR mismatched types [E0308]
// note: do not suggest because of `E: usize`
let x: &Result<usize, usize> = &Ok(3);
let y: Result<&usize, usize> = x;
//~^ ERROR mismatched types [E0308]
let ref opt = Some(Foo);
opt.map(|arg| takes_ref(arg)); //~ ERROR mismatched types [E0308]
opt.and_then(|arg| Some(takes_ref(arg))); //~ ERROR mismatched types [E0308]
let ref opt: Result<_, ()> = Ok(Foo);
opt.map(|arg| takes_ref(arg)); //~ ERROR mismatched types [E0308]
opt.and_then(|arg| Ok(takes_ref(arg))); //~ ERROR mismatched types [E0308]
let x: &Option<usize> = &Some(3);
let y: Option<&usize> = x; //~ ERROR mismatched types [E0308]
let x: &Result<usize, usize> = &Ok(3);
let y: Result<&usize, &usize> = x;
//~^ ERROR mismatched types [E0308]
// note: do not suggest because of `E: usize`
let x: &Result<usize, usize> = &Ok(3);
let y: Result<&usize, usize> = x; //~ ERROR mismatched types [E0308]
}
78 changes: 39 additions & 39 deletions src/test/ui/suggestions/as-ref.stderr
@@ -1,70 +1,70 @@
error[E0308]: mismatched types
--> $DIR/as-ref.rs:6:27
--> $DIR/as-ref.rs:7:29
|
LL | opt.map(|arg| takes_ref(arg));
| --- ^^^ expected `&Foo`, found struct `Foo`
| |
| help: consider using `as_ref` instead: `as_ref().map`
LL | opt.map(|arg| takes_ref(arg));
| --- ^^^ expected `&Foo`, found struct `Foo`
| |
| help: consider using `as_ref` instead: `as_ref().map`

error[E0308]: mismatched types
--> $DIR/as-ref.rs:8:37
--> $DIR/as-ref.rs:8:39
|
LL | opt.and_then(|arg| Some(takes_ref(arg)));
| -------- ^^^ expected `&Foo`, found struct `Foo`
| |
| help: consider using `as_ref` instead: `as_ref().and_then`
LL | opt.and_then(|arg| Some(takes_ref(arg)));
| -------- ^^^ expected `&Foo`, found struct `Foo`
| |
| help: consider using `as_ref` instead: `as_ref().and_then`

error[E0308]: mismatched types
--> $DIR/as-ref.rs:11:27
--> $DIR/as-ref.rs:10:29
|
LL | opt.map(|arg| takes_ref(arg));
| --- ^^^ expected `&Foo`, found struct `Foo`
| |
| help: consider using `as_ref` instead: `as_ref().map`
LL | opt.map(|arg| takes_ref(arg));
| --- ^^^ expected `&Foo`, found struct `Foo`
| |
| help: consider using `as_ref` instead: `as_ref().map`

error[E0308]: mismatched types
--> $DIR/as-ref.rs:13:35
--> $DIR/as-ref.rs:11:37
|
LL | opt.and_then(|arg| Ok(takes_ref(arg)));
| -------- ^^^ expected `&Foo`, found struct `Foo`
| |
| help: consider using `as_ref` instead: `as_ref().and_then`
LL | opt.and_then(|arg| Ok(takes_ref(arg)));
| -------- ^^^ expected `&Foo`, found struct `Foo`
| |
| help: consider using `as_ref` instead: `as_ref().and_then`

error[E0308]: mismatched types
--> $DIR/as-ref.rs:16:27
--> $DIR/as-ref.rs:13:29
|
LL | let y: Option<&usize> = x;
| -------------- ^
| | |
| | expected enum `Option`, found `&Option<usize>`
| | help: you can convert from `&Option<T>` to `Option<&T>` using `.as_ref()`: `x.as_ref()`
| expected due to this
LL | let y: Option<&usize> = x;
| -------------- ^
| | |
| | expected enum `Option`, found `&Option<usize>`
| | help: you can convert from `&Option<T>` to `Option<&T>` using `.as_ref()`: `x.as_ref()`
| expected due to this
|
= note: expected enum `Option<&usize>`
found reference `&Option<usize>`

error[E0308]: mismatched types
--> $DIR/as-ref.rs:19:35
--> $DIR/as-ref.rs:15:37
|
LL | let y: Result<&usize, &usize> = x;
| ---------------------- ^ expected enum `Result`, found reference
| |
| expected due to this
LL | let y: Result<&usize, &usize> = x;
| ---------------------- ^ expected enum `Result`, found reference
| |
| expected due to this
|
= note: expected enum `Result<&usize, &usize>`
found reference `&Result<usize, usize>`
help: you can convert from `&Result<T, E>` to `Result<&T, &E>` using `.as_ref()`
|
LL | let y: Result<&usize, &usize> = x.as_ref();
| ^^^^^^^^^^
LL | let y: Result<&usize, &usize> = x.as_ref();
| ^^^^^^^^^^

error[E0308]: mismatched types
--> $DIR/as-ref.rs:23:34
--> $DIR/as-ref.rs:19:36
|
LL | let y: Result<&usize, usize> = x;
| --------------------- ^ expected enum `Result`, found reference
| |
| expected due to this
LL | let y: Result<&usize, usize> = x;
| --------------------- ^ expected enum `Result`, found reference
| |
| expected due to this
|
= note: expected enum `Result<&usize, usize>`
found reference `&Result<usize, usize>`
Expand Down

0 comments on commit 2763a05

Please sign in to comment.