about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2021-04-20 14:05:12 +0000
committerbors <bors@rust-lang.org>2021-04-20 14:05:12 +0000
commit6df26f897cffb2d86880544bb451c6b5f8509b2d (patch)
treeb0ebb5db69172b0281da1996110317c09402145f
parent7d0132ae90757af425a26a3d69c4d579b9b110a6 (diff)
parent2763a0541cc8293a8fd1ac65fdc3b2977106a0a0 (diff)
downloadrust-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.rs14
-rw-r--r--compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs20
-rw-r--r--src/test/ui/suggestions/as-ref-2.fixed13
-rw-r--r--src/test/ui/suggestions/as-ref-2.rs13
-rw-r--r--src/test/ui/suggestions/as-ref-2.stderr23
-rw-r--r--src/test/ui/suggestions/as-ref.rs35
-rw-r--r--src/test/ui/suggestions/as-ref.stderr78
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>`