diff options
| author | bors <bors@rust-lang.org> | 2021-04-20 14:05:12 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2021-04-20 14:05:12 +0000 |
| commit | 6df26f897cffb2d86880544bb451c6b5f8509b2d (patch) | |
| tree | b0ebb5db69172b0281da1996110317c09402145f | |
| parent | 7d0132ae90757af425a26a3d69c4d579b9b110a6 (diff) | |
| parent | 2763a0541cc8293a8fd1ac65fdc3b2977106a0a0 (diff) | |
| download | rust-6df26f897cffb2d86880544bb451c6b5f8509b2d.tar.gz rust-6df26f897cffb2d86880544bb451c6b5f8509b2d.zip | |
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.
| -rw-r--r-- | compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs | 14 | ||||
| -rw-r--r-- | compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs | 20 | ||||
| -rw-r--r-- | src/test/ui/suggestions/as-ref-2.fixed | 13 | ||||
| -rw-r--r-- | src/test/ui/suggestions/as-ref-2.rs | 13 | ||||
| -rw-r--r-- | src/test/ui/suggestions/as-ref-2.stderr | 23 | ||||
| -rw-r--r-- | src/test/ui/suggestions/as-ref.rs | 35 | ||||
| -rw-r--r-- | src/test/ui/suggestions/as-ref.stderr | 78 |
7 files changed, 134 insertions, 62 deletions
diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs index df6f1963923..9f19a474ca3 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs @@ -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, @@ -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) diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs index 577d7d53814..aa9f18d9996 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs @@ -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`) @@ -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 { diff --git a/src/test/ui/suggestions/as-ref-2.fixed b/src/test/ui/suggestions/as-ref-2.fixed new file mode 100644 index 00000000000..13bbb233f39 --- /dev/null +++ b/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` +} diff --git a/src/test/ui/suggestions/as-ref-2.rs b/src/test/ui/suggestions/as-ref-2.rs new file mode 100644 index 00000000000..74d61cdd95f --- /dev/null +++ b/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` +} diff --git a/src/test/ui/suggestions/as-ref-2.stderr b/src/test/ui/suggestions/as-ref-2.stderr new file mode 100644 index 00000000000..f2eddf2fb09 --- /dev/null +++ b/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`. diff --git a/src/test/ui/suggestions/as-ref.rs b/src/test/ui/suggestions/as-ref.rs index 03f04c389f1..46d9461538c 100644 --- a/src/test/ui/suggestions/as-ref.rs +++ b/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] } diff --git a/src/test/ui/suggestions/as-ref.stderr b/src/test/ui/suggestions/as-ref.stderr index 7e4d7fb3933..dc5d7efd752 100644 --- a/src/test/ui/suggestions/as-ref.stderr +++ b/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>` |
