about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2018-12-30 09:51:44 +0000
committerbors <bors@rust-lang.org>2018-12-30 09:51:44 +0000
commit7155690ffcdf2ce325361bdb5b64ad69c043662f (patch)
tree359546a246c27faefe21f68bde9f4ac35608409a
parent0e6f8987aac22bfeef36d708aa60ed7be6ff89b7 (diff)
parent0ecc128ccb43d0302288011a6919989e91da3bb8 (diff)
downloadrust-7155690ffcdf2ce325361bdb5b64ad69c043662f.tar.gz
rust-7155690ffcdf2ce325361bdb5b64ad69c043662f.zip
Auto merge of #57158 - estebank:as-ref, r=zackmdavis
Suggest `.as_ref()` when appropriate for `Option` and `Result`

Fix #55198.
-rw-r--r--src/librustc/infer/error_reporting/mod.rs67
-rw-r--r--src/librustc/ty/util.rs28
-rw-r--r--src/test/ui/as-ref.stderr47
-rw-r--r--src/test/ui/suggestions/as-ref.rs (renamed from src/test/ui/as-ref.rs)10
-rw-r--r--src/test/ui/suggestions/as-ref.stderr81
5 files changed, 172 insertions, 61 deletions
diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs
index 75407835a1b..e34bb9f4f7b 100644
--- a/src/librustc/infer/error_reporting/mod.rs
+++ b/src/librustc/infer/error_reporting/mod.rs
@@ -956,6 +956,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                                 diag.span_label(span, message);
                             }
                         }
+                        self.suggest_as_ref_where_appropriate(span, &exp_found, diag);
                     }
 
                     diag.note_expected_found(&"type", expected, found);
@@ -972,6 +973,72 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         self.note_error_origin(diag, &cause);
     }
 
+    /// When encountering a case where `.as_ref()` on a `Result` or `Option` would be appropriate,
+    /// suggest it.
+    fn suggest_as_ref_where_appropriate(
+        &self,
+        span: Span,
+        exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
+        diag: &mut DiagnosticBuilder<'tcx>,
+    ) {
+        match (&exp_found.expected.sty, &exp_found.found.sty) {
+            (TyKind::Adt(exp_def, exp_substs), TyKind::Ref(_, found_ty, _)) => {
+                if let TyKind::Adt(found_def, found_substs) = found_ty.sty {
+                    let path_str = format!("{:?}", exp_def);
+                    if exp_def == &found_def {
+                        let opt_msg = "you can convert from `&Option<T>` to `Option<&T>` using \
+                                       `.as_ref()`";
+                        let result_msg = "you can convert from `&Result<T, E>` to \
+                                          `Result<&T, &E>` using `.as_ref()`";
+                        let have_as_ref = &[
+                            ("std::option::Option", opt_msg),
+                            ("core::option::Option", opt_msg),
+                            ("std::result::Result", result_msg),
+                            ("core::result::Result", result_msg),
+                        ];
+                        if let Some(msg) = have_as_ref.iter()
+                            .filter_map(|(path, msg)| if &path_str == path {
+                                Some(msg)
+                            } else {
+                                None
+                            }).next()
+                        {
+                            let mut show_suggestion = true;
+                            for (exp_ty, found_ty) in exp_substs.types().zip(found_substs.types()) {
+                                match exp_ty.sty {
+                                    TyKind::Ref(_, exp_ty, _) => {
+                                        match (&exp_ty.sty, &found_ty.sty) {
+                                            (_, TyKind::Param(_)) |
+                                            (_, TyKind::Infer(_)) |
+                                            (TyKind::Param(_), _) |
+                                            (TyKind::Infer(_), _) => {}
+                                            _ if ty::TyS::same_type(exp_ty, found_ty) => {}
+                                            _ => show_suggestion = false,
+                                        };
+                                    }
+                                    TyKind::Param(_) | TyKind::Infer(_) => {}
+                                    _ => show_suggestion = false,
+                                }
+                            }
+                            if let (Ok(snippet), true) = (
+                                self.tcx.sess.source_map().span_to_snippet(span),
+                                show_suggestion,
+                            ) {
+                                diag.span_suggestion_with_applicability(
+                                    span,
+                                    msg,
+                                    format!("{}.as_ref()", snippet),
+                                    Applicability::MachineApplicable,
+                                );
+                            }
+                        }
+                    }
+                }
+            }
+            _ => {}
+        }
+    }
+
     pub fn report_and_explain_type_error(
         &self,
         trace: TypeTrace<'tcx>,
diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs
index b2e2955619e..1d30ccb87b5 100644
--- a/src/librustc/ty/util.rs
+++ b/src/librustc/ty/util.rs
@@ -658,6 +658,19 @@ impl<'a, 'tcx> ty::TyS<'tcx> {
         tcx.needs_drop_raw(param_env.and(self))
     }
 
+    pub fn same_type(a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
+        match (&a.sty, &b.sty) {
+            (&Adt(did_a, substs_a), &Adt(did_b, substs_b)) => {
+                if did_a != did_b {
+                    return false;
+                }
+
+                substs_a.types().zip(substs_b.types()).all(|(a, b)| Self::same_type(a, b))
+            }
+            _ => a == b,
+        }
+    }
+
     /// Check whether a type is representable. This means it cannot contain unboxed
     /// structural recursion. This check is needed for structs and enums.
     pub fn is_representable(&'tcx self,
@@ -730,19 +743,6 @@ impl<'a, 'tcx> ty::TyS<'tcx> {
             }
         }
 
-        fn same_type<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
-            match (&a.sty, &b.sty) {
-                (&Adt(did_a, substs_a), &Adt(did_b, substs_b)) => {
-                    if did_a != did_b {
-                        return false;
-                    }
-
-                    substs_a.types().zip(substs_b.types()).all(|(a, b)| same_type(a, b))
-                }
-                _ => a == b,
-            }
-        }
-
         // Does the type `ty` directly (without indirection through a pointer)
         // contain any types on stack `seen`?
         fn is_type_structurally_recursive<'a, 'tcx>(
@@ -807,7 +807,7 @@ impl<'a, 'tcx> ty::TyS<'tcx> {
                         // struct Foo { Option<Option<Foo>> }
 
                         for &seen_type in iter {
-                            if same_type(ty, seen_type) {
+                            if ty::TyS::same_type(ty, seen_type) {
                                 debug!("ContainsRecursive: {:?} contains {:?}",
                                        seen_type,
                                        ty);
diff --git a/src/test/ui/as-ref.stderr b/src/test/ui/as-ref.stderr
deleted file mode 100644
index 8ca54e8006c..00000000000
--- a/src/test/ui/as-ref.stderr
+++ /dev/null
@@ -1,47 +0,0 @@
-error[E0308]: mismatched types
-  --> $DIR/as-ref.rs:6:27
-   |
-LL |   opt.map(|arg| takes_ref(arg));
-   |       -                   ^^^ expected &Foo, found struct `Foo`
-   |       |
-   |       help: consider using `as_ref` instead: `as_ref().`
-   |
-   = note: expected type `&Foo`
-              found type `Foo`
-
-error[E0308]: mismatched types
-  --> $DIR/as-ref.rs:8:37
-   |
-LL |   opt.and_then(|arg| Some(takes_ref(arg)));
-   |       -                             ^^^ expected &Foo, found struct `Foo`
-   |       |
-   |       help: consider using `as_ref` instead: `as_ref().`
-   |
-   = note: expected type `&Foo`
-              found type `Foo`
-
-error[E0308]: mismatched types
-  --> $DIR/as-ref.rs:11:27
-   |
-LL |   opt.map(|arg| takes_ref(arg));
-   |       -                   ^^^ expected &Foo, found struct `Foo`
-   |       |
-   |       help: consider using `as_ref` instead: `as_ref().`
-   |
-   = note: expected type `&Foo`
-              found type `Foo`
-
-error[E0308]: mismatched types
-  --> $DIR/as-ref.rs:13:35
-   |
-LL |   opt.and_then(|arg| Ok(takes_ref(arg)));
-   |       -                           ^^^ expected &Foo, found struct `Foo`
-   |       |
-   |       help: consider using `as_ref` instead: `as_ref().`
-   |
-   = note: expected type `&Foo`
-              found type `Foo`
-
-error: aborting due to 4 previous errors
-
-For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/as-ref.rs b/src/test/ui/suggestions/as-ref.rs
index 822d10f2ca7..03f04c389f1 100644
--- a/src/test/ui/as-ref.rs
+++ b/src/test/ui/suggestions/as-ref.rs
@@ -12,4 +12,14 @@ fn main() {
   //~^ 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
new file mode 100644
index 00000000000..7273496a7ce
--- /dev/null
+++ b/src/test/ui/suggestions/as-ref.stderr
@@ -0,0 +1,81 @@
+error[E0308]: mismatched types
+  --> $DIR/as-ref.rs:6:27
+   |
+LL |   opt.map(|arg| takes_ref(arg));
+   |       -                   ^^^ expected &Foo, found struct `Foo`
+   |       |
+   |       help: consider using `as_ref` instead: `as_ref().`
+   |
+   = note: expected type `&Foo`
+              found type `Foo`
+
+error[E0308]: mismatched types
+  --> $DIR/as-ref.rs:8:37
+   |
+LL |   opt.and_then(|arg| Some(takes_ref(arg)));
+   |       -                             ^^^ expected &Foo, found struct `Foo`
+   |       |
+   |       help: consider using `as_ref` instead: `as_ref().`
+   |
+   = note: expected type `&Foo`
+              found type `Foo`
+
+error[E0308]: mismatched types
+  --> $DIR/as-ref.rs:11:27
+   |
+LL |   opt.map(|arg| takes_ref(arg));
+   |       -                   ^^^ expected &Foo, found struct `Foo`
+   |       |
+   |       help: consider using `as_ref` instead: `as_ref().`
+   |
+   = note: expected type `&Foo`
+              found type `Foo`
+
+error[E0308]: mismatched types
+  --> $DIR/as-ref.rs:13:35
+   |
+LL |   opt.and_then(|arg| Ok(takes_ref(arg)));
+   |       -                           ^^^ expected &Foo, found struct `Foo`
+   |       |
+   |       help: consider using `as_ref` instead: `as_ref().`
+   |
+   = note: expected type `&Foo`
+              found type `Foo`
+
+error[E0308]: mismatched types
+  --> $DIR/as-ref.rs:16:27
+   |
+LL |   let y: Option<&usize> = x;
+   |                           ^
+   |                           |
+   |                           expected enum `std::option::Option`, found reference
+   |                           help: you can convert from `&Option<T>` to `Option<&T>` using `.as_ref()`: `x.as_ref()`
+   |
+   = note: expected type `std::option::Option<&usize>`
+              found type `&std::option::Option<usize>`
+
+error[E0308]: mismatched types
+  --> $DIR/as-ref.rs:19:35
+   |
+LL |   let y: Result<&usize, &usize> = x;
+   |                                   ^ expected enum `std::result::Result`, found reference
+   |
+   = note: expected type `std::result::Result<&usize, &usize>`
+              found type `&std::result::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();
+   |                                   ^^^^^^^^^^
+
+error[E0308]: mismatched types
+  --> $DIR/as-ref.rs:23:34
+   |
+LL |   let y: Result<&usize, usize> = x;
+   |                                  ^ expected enum `std::result::Result`, found reference
+   |
+   = note: expected type `std::result::Result<&usize, usize>`
+              found type `&std::result::Result<usize, usize>`
+
+error: aborting due to 7 previous errors
+
+For more information about this error, try `rustc --explain E0308`.