Auto merge of #84353 - estebank:as-ref-mir, r=davidtwco
Suggest `.as_ref()` on borrow error involving `Option`/`Result` 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.
This commit is contained in:
commit
6df26f897c
7 changed files with 134 additions and 62 deletions
|
@ -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 {
|
if implicit_into_iter {
|
||||||
err.span_label(
|
err.span_label(
|
||||||
fn_call_span,
|
fn_call_span,
|
||||||
|
@ -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
|
// Avoid pointing to the same function in multiple different
|
||||||
// error messages.
|
// error messages.
|
||||||
if span != DUMMY_SP && self.fn_self_span_reported.insert(self_arg.span)
|
if span != DUMMY_SP && self.fn_self_span_reported.insert(self_arg.span)
|
||||||
|
|
|
@ -573,7 +573,13 @@ pub(super) enum UseSpans<'tcx> {
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||||
pub(super) enum FnSelfUseKind<'tcx> {
|
pub(super) enum FnSelfUseKind<'tcx> {
|
||||||
/// A normal method call of the form `receiver.foo(a, b, c)`
|
/// 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)`
|
/// A call to `FnOnce::call_once`, desugared from `my_closure(a, b, c)`
|
||||||
FnOnceCall,
|
FnOnceCall,
|
||||||
/// A call to an operator trait, desuraged from operator syntax (e.g. `a << b`)
|
/// A call to an operator trait, desuraged from operator syntax (e.g. `a << b`)
|
||||||
|
@ -900,7 +906,17 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||||
fn_call_span.desugaring_kind(),
|
fn_call_span.desugaring_kind(),
|
||||||
Some(DesugaringKind::ForLoop(ForLoopLoc::IntoIter))
|
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 {
|
return FnSelfUse {
|
||||||
|
|
13
src/test/ui/suggestions/as-ref-2.fixed
Normal file
13
src/test/ui/suggestions/as-ref-2.fixed
Normal file
|
@ -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
src/test/ui/suggestions/as-ref-2.rs
Normal file
13
src/test/ui/suggestions/as-ref-2.rs
Normal file
|
@ -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
src/test/ui/suggestions/as-ref-2.stderr
Normal file
23
src/test/ui/suggestions/as-ref-2.stderr
Normal file
|
@ -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`.
|
|
@ -1,25 +1,20 @@
|
||||||
struct Foo;
|
struct Foo;
|
||||||
|
|
||||||
fn takes_ref(_: &Foo) {}
|
fn takes_ref(_: &Foo) {}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let ref opt = Some(Foo);
|
let ref opt = Some(Foo);
|
||||||
opt.map(|arg| takes_ref(arg));
|
opt.map(|arg| takes_ref(arg)); //~ ERROR mismatched types [E0308]
|
||||||
//~^ ERROR mismatched types [E0308]
|
opt.and_then(|arg| Some(takes_ref(arg))); //~ ERROR mismatched types [E0308]
|
||||||
opt.and_then(|arg| Some(takes_ref(arg)));
|
let ref opt: Result<_, ()> = Ok(Foo);
|
||||||
//~^ ERROR mismatched types [E0308]
|
opt.map(|arg| takes_ref(arg)); //~ ERROR mismatched types [E0308]
|
||||||
let ref opt: Result<_, ()> = Ok(Foo);
|
opt.and_then(|arg| Ok(takes_ref(arg))); //~ ERROR mismatched types [E0308]
|
||||||
opt.map(|arg| takes_ref(arg));
|
let x: &Option<usize> = &Some(3);
|
||||||
//~^ ERROR mismatched types [E0308]
|
let y: Option<&usize> = x; //~ ERROR mismatched types [E0308]
|
||||||
opt.and_then(|arg| Ok(takes_ref(arg)));
|
let x: &Result<usize, usize> = &Ok(3);
|
||||||
//~^ ERROR mismatched types [E0308]
|
let y: Result<&usize, &usize> = x;
|
||||||
let x: &Option<usize> = &Some(3);
|
//~^ ERROR mismatched types [E0308]
|
||||||
let y: Option<&usize> = x;
|
// note: do not suggest because of `E: usize`
|
||||||
//~^ ERROR mismatched types [E0308]
|
let x: &Result<usize, usize> = &Ok(3);
|
||||||
let x: &Result<usize, usize> = &Ok(3);
|
let y: Result<&usize, usize> = x; //~ ERROR mismatched types [E0308]
|
||||||
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]
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,70 +1,70 @@
|
||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
--> $DIR/as-ref.rs:6:27
|
--> $DIR/as-ref.rs:7:29
|
||||||
|
|
|
|
||||||
LL | opt.map(|arg| takes_ref(arg));
|
LL | opt.map(|arg| takes_ref(arg));
|
||||||
| --- ^^^ expected `&Foo`, found struct `Foo`
|
| --- ^^^ expected `&Foo`, found struct `Foo`
|
||||||
| |
|
| |
|
||||||
| help: consider using `as_ref` instead: `as_ref().map`
|
| help: consider using `as_ref` instead: `as_ref().map`
|
||||||
|
|
||||||
error[E0308]: mismatched types
|
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)));
|
LL | opt.and_then(|arg| Some(takes_ref(arg)));
|
||||||
| -------- ^^^ expected `&Foo`, found struct `Foo`
|
| -------- ^^^ expected `&Foo`, found struct `Foo`
|
||||||
| |
|
| |
|
||||||
| help: consider using `as_ref` instead: `as_ref().and_then`
|
| help: consider using `as_ref` instead: `as_ref().and_then`
|
||||||
|
|
||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
--> $DIR/as-ref.rs:11:27
|
--> $DIR/as-ref.rs:10:29
|
||||||
|
|
|
|
||||||
LL | opt.map(|arg| takes_ref(arg));
|
LL | opt.map(|arg| takes_ref(arg));
|
||||||
| --- ^^^ expected `&Foo`, found struct `Foo`
|
| --- ^^^ expected `&Foo`, found struct `Foo`
|
||||||
| |
|
| |
|
||||||
| help: consider using `as_ref` instead: `as_ref().map`
|
| help: consider using `as_ref` instead: `as_ref().map`
|
||||||
|
|
||||||
error[E0308]: mismatched types
|
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)));
|
LL | opt.and_then(|arg| Ok(takes_ref(arg)));
|
||||||
| -------- ^^^ expected `&Foo`, found struct `Foo`
|
| -------- ^^^ expected `&Foo`, found struct `Foo`
|
||||||
| |
|
| |
|
||||||
| help: consider using `as_ref` instead: `as_ref().and_then`
|
| help: consider using `as_ref` instead: `as_ref().and_then`
|
||||||
|
|
||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
--> $DIR/as-ref.rs:16:27
|
--> $DIR/as-ref.rs:13:29
|
||||||
|
|
|
|
||||||
LL | let y: Option<&usize> = x;
|
LL | let y: Option<&usize> = x;
|
||||||
| -------------- ^
|
| -------------- ^
|
||||||
| | |
|
| | |
|
||||||
| | expected enum `Option`, found `&Option<usize>`
|
| | expected enum `Option`, found `&Option<usize>`
|
||||||
| | help: you can convert from `&Option<T>` to `Option<&T>` using `.as_ref()`: `x.as_ref()`
|
| | help: you can convert from `&Option<T>` to `Option<&T>` using `.as_ref()`: `x.as_ref()`
|
||||||
| expected due to this
|
| expected due to this
|
||||||
|
|
|
|
||||||
= note: expected enum `Option<&usize>`
|
= note: expected enum `Option<&usize>`
|
||||||
found reference `&Option<usize>`
|
found reference `&Option<usize>`
|
||||||
|
|
||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
--> $DIR/as-ref.rs:19:35
|
--> $DIR/as-ref.rs:15:37
|
||||||
|
|
|
|
||||||
LL | let y: Result<&usize, &usize> = x;
|
LL | let y: Result<&usize, &usize> = x;
|
||||||
| ---------------------- ^ expected enum `Result`, found reference
|
| ---------------------- ^ expected enum `Result`, found reference
|
||||||
| |
|
| |
|
||||||
| expected due to this
|
| expected due to this
|
||||||
|
|
|
|
||||||
= note: expected enum `Result<&usize, &usize>`
|
= note: expected enum `Result<&usize, &usize>`
|
||||||
found reference `&Result<usize, usize>`
|
found reference `&Result<usize, usize>`
|
||||||
help: you can convert from `&Result<T, E>` to `Result<&T, &E>` using `.as_ref()`
|
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
|
error[E0308]: mismatched types
|
||||||
--> $DIR/as-ref.rs:23:34
|
--> $DIR/as-ref.rs:19:36
|
||||||
|
|
|
|
||||||
LL | let y: Result<&usize, usize> = x;
|
LL | let y: Result<&usize, usize> = x;
|
||||||
| --------------------- ^ expected enum `Result`, found reference
|
| --------------------- ^ expected enum `Result`, found reference
|
||||||
| |
|
| |
|
||||||
| expected due to this
|
| expected due to this
|
||||||
|
|
|
|
||||||
= note: expected enum `Result<&usize, usize>`
|
= note: expected enum `Result<&usize, usize>`
|
||||||
found reference `&Result<usize, usize>`
|
found reference `&Result<usize, usize>`
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue