Skip to content

Commit 6df26f8

Browse files
committed
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.
2 parents 7d0132a + 2763a05 commit 6df26f8

File tree

7 files changed

+134
-62
lines changed

7 files changed

+134
-62
lines changed

compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs

+13-1
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
197197
);
198198
}
199199
}
200-
FnSelfUseKind::Normal { self_arg, implicit_into_iter } => {
200+
FnSelfUseKind::Normal {
201+
self_arg,
202+
implicit_into_iter,
203+
is_option_or_result,
204+
} => {
201205
if implicit_into_iter {
202206
err.span_label(
203207
fn_call_span,
@@ -215,6 +219,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
215219
),
216220
);
217221
}
222+
if is_option_or_result {
223+
err.span_suggestion_verbose(
224+
fn_call_span.shrink_to_lo(),
225+
"consider calling `.as_ref()` to borrow the type's contents",
226+
"as_ref().".to_string(),
227+
Applicability::MachineApplicable,
228+
);
229+
}
218230
// Avoid pointing to the same function in multiple different
219231
// error messages.
220232
if span != DUMMY_SP && self.fn_self_span_reported.insert(self_arg.span)

compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs

+18-2
Original file line numberDiff line numberDiff line change
@@ -573,7 +573,13 @@ pub(super) enum UseSpans<'tcx> {
573573
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
574574
pub(super) enum FnSelfUseKind<'tcx> {
575575
/// A normal method call of the form `receiver.foo(a, b, c)`
576-
Normal { self_arg: Ident, implicit_into_iter: bool },
576+
Normal {
577+
self_arg: Ident,
578+
implicit_into_iter: bool,
579+
/// Whether the self type of the method call has an `.as_ref()` method.
580+
/// Used for better diagnostics.
581+
is_option_or_result: bool,
582+
},
577583
/// A call to `FnOnce::call_once`, desugared from `my_closure(a, b, c)`
578584
FnOnceCall,
579585
/// A call to an operator trait, desuraged from operator syntax (e.g. `a << b`)
@@ -900,7 +906,17 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
900906
fn_call_span.desugaring_kind(),
901907
Some(DesugaringKind::ForLoop(ForLoopLoc::IntoIter))
902908
);
903-
FnSelfUseKind::Normal { self_arg, implicit_into_iter }
909+
let parent_self_ty = parent
910+
.filter(|did| tcx.def_kind(*did) == rustc_hir::def::DefKind::Impl)
911+
.and_then(|did| match tcx.type_of(did).kind() {
912+
ty::Adt(def, ..) => Some(def.did),
913+
_ => None,
914+
});
915+
let is_option_or_result = parent_self_ty.map_or(false, |def_id| {
916+
tcx.is_diagnostic_item(sym::option_type, def_id)
917+
|| tcx.is_diagnostic_item(sym::result_type, def_id)
918+
});
919+
FnSelfUseKind::Normal { self_arg, implicit_into_iter, is_option_or_result }
904920
});
905921

906922
return FnSelfUse {
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// run-rustfix
2+
3+
struct Struct;
4+
5+
fn bar(_: &Struct) -> Struct {
6+
Struct
7+
}
8+
9+
fn main() {
10+
let foo = Some(Struct);
11+
let _x: Option<Struct> = foo.as_ref().map(|s| bar(&s));
12+
let _y = foo; //~ERROR use of moved value: `foo`
13+
}

src/test/ui/suggestions/as-ref-2.rs

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// run-rustfix
2+
3+
struct Struct;
4+
5+
fn bar(_: &Struct) -> Struct {
6+
Struct
7+
}
8+
9+
fn main() {
10+
let foo = Some(Struct);
11+
let _x: Option<Struct> = foo.map(|s| bar(&s));
12+
let _y = foo; //~ERROR use of moved value: `foo`
13+
}
+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
error[E0382]: use of moved value: `foo`
2+
--> $DIR/as-ref-2.rs:12:14
3+
|
4+
LL | let foo = Some(Struct);
5+
| --- move occurs because `foo` has type `Option<Struct>`, which does not implement the `Copy` trait
6+
LL | let _x: Option<Struct> = foo.map(|s| bar(&s));
7+
| ---------------- `foo` moved due to this method call
8+
LL | let _y = foo;
9+
| ^^^ value used here after move
10+
|
11+
note: this function takes ownership of the receiver `self`, which moves `foo`
12+
--> $SRC_DIR/core/src/option.rs:LL:COL
13+
|
14+
LL | pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Option<U> {
15+
| ^^^^
16+
help: consider calling `.as_ref()` to borrow the type's contents
17+
|
18+
LL | let _x: Option<Struct> = foo.as_ref().map(|s| bar(&s));
19+
| ^^^^^^^^^
20+
21+
error: aborting due to previous error
22+
23+
For more information about this error, try `rustc --explain E0382`.

src/test/ui/suggestions/as-ref.rs

+15-20
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,20 @@
11
struct Foo;
2+
23
fn takes_ref(_: &Foo) {}
34

45
fn main() {
5-
let ref opt = Some(Foo);
6-
opt.map(|arg| takes_ref(arg));
7-
//~^ ERROR mismatched types [E0308]
8-
opt.and_then(|arg| Some(takes_ref(arg)));
9-
//~^ ERROR mismatched types [E0308]
10-
let ref opt: Result<_, ()> = Ok(Foo);
11-
opt.map(|arg| takes_ref(arg));
12-
//~^ ERROR mismatched types [E0308]
13-
opt.and_then(|arg| Ok(takes_ref(arg)));
14-
//~^ ERROR mismatched types [E0308]
15-
let x: &Option<usize> = &Some(3);
16-
let y: Option<&usize> = x;
17-
//~^ ERROR mismatched types [E0308]
18-
let x: &Result<usize, usize> = &Ok(3);
19-
let y: Result<&usize, &usize> = x;
20-
//~^ ERROR mismatched types [E0308]
21-
// note: do not suggest because of `E: usize`
22-
let x: &Result<usize, usize> = &Ok(3);
23-
let y: Result<&usize, usize> = x;
24-
//~^ ERROR mismatched types [E0308]
6+
let ref opt = Some(Foo);
7+
opt.map(|arg| takes_ref(arg)); //~ ERROR mismatched types [E0308]
8+
opt.and_then(|arg| Some(takes_ref(arg))); //~ ERROR mismatched types [E0308]
9+
let ref opt: Result<_, ()> = Ok(Foo);
10+
opt.map(|arg| takes_ref(arg)); //~ ERROR mismatched types [E0308]
11+
opt.and_then(|arg| Ok(takes_ref(arg))); //~ ERROR mismatched types [E0308]
12+
let x: &Option<usize> = &Some(3);
13+
let y: Option<&usize> = x; //~ ERROR mismatched types [E0308]
14+
let x: &Result<usize, usize> = &Ok(3);
15+
let y: Result<&usize, &usize> = x;
16+
//~^ ERROR mismatched types [E0308]
17+
// note: do not suggest because of `E: usize`
18+
let x: &Result<usize, usize> = &Ok(3);
19+
let y: Result<&usize, usize> = x; //~ ERROR mismatched types [E0308]
2520
}

src/test/ui/suggestions/as-ref.stderr

+39-39
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,70 @@
11
error[E0308]: mismatched types
2-
--> $DIR/as-ref.rs:6:27
2+
--> $DIR/as-ref.rs:7:29
33
|
4-
LL | opt.map(|arg| takes_ref(arg));
5-
| --- ^^^ expected `&Foo`, found struct `Foo`
6-
| |
7-
| help: consider using `as_ref` instead: `as_ref().map`
4+
LL | opt.map(|arg| takes_ref(arg));
5+
| --- ^^^ expected `&Foo`, found struct `Foo`
6+
| |
7+
| help: consider using `as_ref` instead: `as_ref().map`
88

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

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

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

3333
error[E0308]: mismatched types
34-
--> $DIR/as-ref.rs:16:27
34+
--> $DIR/as-ref.rs:13:29
3535
|
36-
LL | let y: Option<&usize> = x;
37-
| -------------- ^
38-
| | |
39-
| | expected enum `Option`, found `&Option<usize>`
40-
| | help: you can convert from `&Option<T>` to `Option<&T>` using `.as_ref()`: `x.as_ref()`
41-
| expected due to this
36+
LL | let y: Option<&usize> = x;
37+
| -------------- ^
38+
| | |
39+
| | expected enum `Option`, found `&Option<usize>`
40+
| | help: you can convert from `&Option<T>` to `Option<&T>` using `.as_ref()`: `x.as_ref()`
41+
| expected due to this
4242
|
4343
= note: expected enum `Option<&usize>`
4444
found reference `&Option<usize>`
4545

4646
error[E0308]: mismatched types
47-
--> $DIR/as-ref.rs:19:35
47+
--> $DIR/as-ref.rs:15:37
4848
|
49-
LL | let y: Result<&usize, &usize> = x;
50-
| ---------------------- ^ expected enum `Result`, found reference
51-
| |
52-
| expected due to this
49+
LL | let y: Result<&usize, &usize> = x;
50+
| ---------------------- ^ expected enum `Result`, found reference
51+
| |
52+
| expected due to this
5353
|
5454
= note: expected enum `Result<&usize, &usize>`
5555
found reference `&Result<usize, usize>`
5656
help: you can convert from `&Result<T, E>` to `Result<&T, &E>` using `.as_ref()`
5757
|
58-
LL | let y: Result<&usize, &usize> = x.as_ref();
59-
| ^^^^^^^^^^
58+
LL | let y: Result<&usize, &usize> = x.as_ref();
59+
| ^^^^^^^^^^
6060

6161
error[E0308]: mismatched types
62-
--> $DIR/as-ref.rs:23:34
62+
--> $DIR/as-ref.rs:19:36
6363
|
64-
LL | let y: Result<&usize, usize> = x;
65-
| --------------------- ^ expected enum `Result`, found reference
66-
| |
67-
| expected due to this
64+
LL | let y: Result<&usize, usize> = x;
65+
| --------------------- ^ expected enum `Result`, found reference
66+
| |
67+
| expected due to this
6868
|
6969
= note: expected enum `Result<&usize, usize>`
7070
found reference `&Result<usize, usize>`

0 commit comments

Comments
 (0)