Skip to content

Suggest .as_ref() on borrow error involving Option/Result #84353

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

Merged
merged 1 commit into from
Apr 20, 2021
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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