about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMazdak Farrokhzad <twingoow@gmail.com>2019-12-06 23:26:57 +0100
committerGitHub <noreply@github.com>2019-12-06 23:26:57 +0100
commit99fee7896b9a5e8e21af1ba6787b21566e945d78 (patch)
tree7ceebe74dd71b38787ef95abd9a170416dd405d8
parent62528d86b01f6d595759d9974413fd2c9055af30 (diff)
parenta6883c0ab00042f63c7e4cec3066577f3f8b8e5c (diff)
downloadrust-99fee7896b9a5e8e21af1ba6787b21566e945d78.tar.gz
rust-99fee7896b9a5e8e21af1ba6787b21566e945d78.zip
Rollup merge of #67009 - Aaron1011:fix/coerce-suggestion, r=Centril
Emit coercion suggestions in more places

Fixes #66910

We have several different kinds of suggestions we can try to make when
type coercion fails. However, we were previously only emitting these
suggestions from `demand_coerce_diag`. This resulted in the compiler
failing to emit applicable suggestions in several different cases, such
as when the implicit return value of a function had the wrong type.

This commit adds a new `emit_coerce_suggestions` method, which tries to
emit a number of related suggestions. This method is called from both
`demand_coerce_diag` and `CoerceMany::coerce_inner`, which covers a much
wider range of cases than before.

We now suggest using `.await` in more cases where it is applicable,
among other improvements.

I'm not happy about disabling the `issue-59756`, but from what I can tell, the suggestion infrastructure in rustc lacks any way of indicating mutually exclusive suggestions (and compiletest lacks a way to only apply a subset of available suggestions).
-rw-r--r--src/librustc_typeck/check/coercion.rs4
-rw-r--r--src/librustc_typeck/check/demand.rs22
-rw-r--r--src/librustc_typeck/check/mod.rs8
-rw-r--r--src/test/ui/async-await/suggest-missing-await.fixed11
-rw-r--r--src/test/ui/async-await/suggest-missing-await.rs11
-rw-r--r--src/test/ui/async-await/suggest-missing-await.stderr19
-rw-r--r--src/test/ui/fully-qualified-type/fully-qualified-type-name4.stderr5
-rw-r--r--src/test/ui/issues/issue-51632-try-desugar-incompatible-types.stderr13
-rw-r--r--src/test/ui/issues/issue-59756.rs4
-rw-r--r--src/test/ui/issues/issue-59756.stderr13
-rw-r--r--src/test/ui/mismatched_types/abridged.stderr10
-rw-r--r--src/test/ui/proc-macro/span-preservation.stderr5
-rw-r--r--src/test/ui/tail-typeck.stderr5
-rw-r--r--src/test/ui/wrong-ret-type.stderr5
14 files changed, 116 insertions, 19 deletions
diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs
index 901a2192e20..253fc5575c5 100644
--- a/src/librustc_typeck/check/coercion.rs
+++ b/src/librustc_typeck/check/coercion.rs
@@ -1284,6 +1284,10 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
                     augment_error(&mut err);
                 }
 
+                if let Some(expr) = expression {
+                    fcx.emit_coerce_suggestions(&mut err, expr, found, expected);
+                }
+
                 // Error possibly reported in `check_assign` so avoid emitting error again.
                 err.emit_unless(expression.filter(|e| fcx.is_assign_to_bool(e, expected))
                     .is_some());
diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs
index 32df6c4636c..16a55d2a4d3 100644
--- a/src/librustc_typeck/check/demand.rs
+++ b/src/librustc_typeck/check/demand.rs
@@ -15,6 +15,22 @@ use errors::{Applicability, DiagnosticBuilder};
 use super::method::probe;
 
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
+
+    pub fn emit_coerce_suggestions(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        expr: &hir::Expr,
+        expr_ty: Ty<'tcx>,
+        expected: Ty<'tcx>
+    ) {
+        self.annotate_expected_due_to_let_ty(err, expr);
+        self.suggest_compatible_variants(err, expr, expected, expr_ty);
+        self.suggest_ref_or_into(err, expr, expected, expr_ty);
+        self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty);
+        self.suggest_missing_await(err, expr, expected, expr_ty);
+    }
+
+
     // Requires that the two types unify, and prints an error message if
     // they don't.
     pub fn demand_suptype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) {
@@ -137,11 +153,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             return (expected, None)
         }
 
-        self.annotate_expected_due_to_let_ty(&mut err, expr);
-        self.suggest_compatible_variants(&mut err, expr, expected, expr_ty);
-        self.suggest_ref_or_into(&mut err, expr, expected, expr_ty);
-        self.suggest_boxing_when_appropriate(&mut err, expr, expected, expr_ty);
-        self.suggest_missing_await(&mut err, expr, expected, expr_ty);
+        self.emit_coerce_suggestions(&mut err, expr, expr_ty, expected);
 
         (expected, Some(err))
     }
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index c7a0190a1d1..a956aba4f62 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -4584,8 +4584,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             pointing_at_return_type = self.suggest_missing_return_type(
                 err, &fn_decl, expected, found, can_suggest);
         }
-        self.suggest_ref_or_into(err, expr, expected, found);
-        self.suggest_boxing_when_appropriate(err, expr, expected, found);
         pointing_at_return_type
     }
 
@@ -4957,7 +4955,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     ty: expected,
                 }));
                 let obligation = traits::Obligation::new(self.misc(sp), self.param_env, predicate);
+                debug!("suggest_missing_await: trying obligation {:?}", obligation);
                 if self.infcx.predicate_may_hold(&obligation) {
+                    debug!("suggest_missing_await: obligation held: {:?}", obligation);
                     if let Ok(code) = self.sess().source_map().span_to_snippet(sp) {
                         err.span_suggestion(
                             sp,
@@ -4965,7 +4965,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             format!("{}.await", code),
                             Applicability::MaybeIncorrect,
                         );
+                    } else {
+                        debug!("suggest_missing_await: no snippet for {:?}", sp);
                     }
+                } else {
+                    debug!("suggest_missing_await: obligation did not hold: {:?}", obligation)
                 }
             }
         }
diff --git a/src/test/ui/async-await/suggest-missing-await.fixed b/src/test/ui/async-await/suggest-missing-await.fixed
index 7c02a907ce7..1ec59d90620 100644
--- a/src/test/ui/async-await/suggest-missing-await.fixed
+++ b/src/test/ui/async-await/suggest-missing-await.fixed
@@ -16,4 +16,15 @@ async fn suggest_await_in_async_fn() {
     //~| SUGGESTION x.await
 }
 
+async fn dummy() {}
+
+#[allow(unused)]
+async fn suggest_await_in_async_fn_return() {
+    dummy().await;
+    //~^ ERROR mismatched types [E0308]
+    //~| HELP try adding a semicolon
+    //~| HELP consider using `.await` here
+    //~| SUGGESTION dummy().await
+}
+
 fn main() {}
diff --git a/src/test/ui/async-await/suggest-missing-await.rs b/src/test/ui/async-await/suggest-missing-await.rs
index 91abd44e65c..70cc1f1d5a2 100644
--- a/src/test/ui/async-await/suggest-missing-await.rs
+++ b/src/test/ui/async-await/suggest-missing-await.rs
@@ -16,4 +16,15 @@ async fn suggest_await_in_async_fn() {
     //~| SUGGESTION x.await
 }
 
+async fn dummy() {}
+
+#[allow(unused)]
+async fn suggest_await_in_async_fn_return() {
+    dummy()
+    //~^ ERROR mismatched types [E0308]
+    //~| HELP try adding a semicolon
+    //~| HELP consider using `.await` here
+    //~| SUGGESTION dummy().await
+}
+
 fn main() {}
diff --git a/src/test/ui/async-await/suggest-missing-await.stderr b/src/test/ui/async-await/suggest-missing-await.stderr
index 7a635a37107..7ab024434b2 100644
--- a/src/test/ui/async-await/suggest-missing-await.stderr
+++ b/src/test/ui/async-await/suggest-missing-await.stderr
@@ -10,6 +10,23 @@ LL |     take_u32(x)
    = note:     expected type `u32`
            found opaque type `impl std::future::Future`
 
-error: aborting due to previous error
+error[E0308]: mismatched types
+  --> $DIR/suggest-missing-await.rs:23:5
+   |
+LL |     dummy()
+   |     ^^^^^^^ expected `()`, found opaque type
+   |
+   = note: expected unit type `()`
+            found opaque type `impl std::future::Future`
+help: try adding a semicolon
+   |
+LL |     dummy();
+   |            ^
+help: consider using `.await` here
+   |
+LL |     dummy().await
+   |
+
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/fully-qualified-type/fully-qualified-type-name4.stderr b/src/test/ui/fully-qualified-type/fully-qualified-type-name4.stderr
index b388f38a7fa..ca61fb0c171 100644
--- a/src/test/ui/fully-qualified-type/fully-qualified-type-name4.stderr
+++ b/src/test/ui/fully-qualified-type/fully-qualified-type-name4.stderr
@@ -4,7 +4,10 @@ error[E0308]: mismatched types
 LL | fn bar(x: usize) -> Option<usize> {
    |                     ------------- expected `std::option::Option<usize>` because of return type
 LL |     return x;
-   |            ^ expected enum `std::option::Option`, found `usize`
+   |            ^
+   |            |
+   |            expected enum `std::option::Option`, found `usize`
+   |            help: try using a variant of the expected enum: `Some(x)`
    |
    = note: expected enum `std::option::Option<usize>`
               found type `usize`
diff --git a/src/test/ui/issues/issue-51632-try-desugar-incompatible-types.stderr b/src/test/ui/issues/issue-51632-try-desugar-incompatible-types.stderr
index aef6dc54747..9ca983df30a 100644
--- a/src/test/ui/issues/issue-51632-try-desugar-incompatible-types.stderr
+++ b/src/test/ui/issues/issue-51632-try-desugar-incompatible-types.stderr
@@ -2,13 +2,18 @@ error[E0308]: try expression alternatives have incompatible types
   --> $DIR/issue-51632-try-desugar-incompatible-types.rs:8:5
    |
 LL |     missing_discourses()?
-   |     ^^^^^^^^^^^^^^^^^^^^-
-   |     |                   |
-   |     |                   help: try removing this `?`
-   |     expected enum `std::result::Result`, found `isize`
+   |     ^^^^^^^^^^^^^^^^^^^^^ expected enum `std::result::Result`, found `isize`
    |
    = note: expected enum `std::result::Result<isize, ()>`
               found type `isize`
+help: try removing this `?`
+   |
+LL |     missing_discourses()
+   |                        --
+help: try using a variant of the expected enum
+   |
+LL |     Ok(missing_discourses()?)
+   |
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/issues/issue-59756.rs b/src/test/ui/issues/issue-59756.rs
index cccae396b72..d6df0592be3 100644
--- a/src/test/ui/issues/issue-59756.rs
+++ b/src/test/ui/issues/issue-59756.rs
@@ -1,4 +1,8 @@
 // run-rustfix
+// ignore-test
+//
+// FIXME: Re-enable this test once we support choosing
+// between multiple mutually exclusive suggestions for the same span
 
 #![allow(warnings)]
 
diff --git a/src/test/ui/issues/issue-59756.stderr b/src/test/ui/issues/issue-59756.stderr
index 150916c0366..9066e57aabf 100644
--- a/src/test/ui/issues/issue-59756.stderr
+++ b/src/test/ui/issues/issue-59756.stderr
@@ -2,13 +2,18 @@ error[E0308]: try expression alternatives have incompatible types
   --> $DIR/issue-59756.rs:13:5
    |
 LL |     foo()?
-   |     ^^^^^-
-   |     |    |
-   |     |    help: try removing this `?`
-   |     expected enum `std::result::Result`, found struct `A`
+   |     ^^^^^^ expected enum `std::result::Result`, found struct `A`
    |
    = note: expected enum `std::result::Result<A, B>`
             found struct `A`
+help: try removing this `?`
+   |
+LL |     foo()
+   |         --
+help: try using a variant of the expected enum
+   |
+LL |     Ok(foo()?)
+   |
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/mismatched_types/abridged.stderr b/src/test/ui/mismatched_types/abridged.stderr
index c2081878629..c73130643db 100644
--- a/src/test/ui/mismatched_types/abridged.stderr
+++ b/src/test/ui/mismatched_types/abridged.stderr
@@ -26,7 +26,10 @@ error[E0308]: mismatched types
 LL | fn b() -> Option<Foo> {
    |           ----------- expected `std::option::Option<Foo>` because of return type
 LL |     Foo { bar: 1 }
-   |     ^^^^^^^^^^^^^^ expected enum `std::option::Option`, found struct `Foo`
+   |     ^^^^^^^^^^^^^^
+   |     |
+   |     expected enum `std::option::Option`, found struct `Foo`
+   |     help: try using a variant of the expected enum: `Some(Foo { bar: 1 })`
    |
    = note: expected enum `std::option::Option<Foo>`
             found struct `Foo`
@@ -37,7 +40,10 @@ error[E0308]: mismatched types
 LL | fn c() -> Result<Foo, Bar> {
    |           ---------------- expected `std::result::Result<Foo, Bar>` because of return type
 LL |     Foo { bar: 1 }
-   |     ^^^^^^^^^^^^^^ expected enum `std::result::Result`, found struct `Foo`
+   |     ^^^^^^^^^^^^^^
+   |     |
+   |     expected enum `std::result::Result`, found struct `Foo`
+   |     help: try using a variant of the expected enum: `Ok(Foo { bar: 1 })`
    |
    = note: expected enum `std::result::Result<Foo, Bar>`
             found struct `Foo`
diff --git a/src/test/ui/proc-macro/span-preservation.stderr b/src/test/ui/proc-macro/span-preservation.stderr
index cd6f0ea10ea..a77e92022e2 100644
--- a/src/test/ui/proc-macro/span-preservation.stderr
+++ b/src/test/ui/proc-macro/span-preservation.stderr
@@ -14,6 +14,11 @@ LL | fn b(x: Option<isize>) -> usize {
 LL |     match x {
 LL |         Some(x) => { return x },
    |                             ^ expected `usize`, found `isize`
+   |
+help: you can convert an `isize` to `usize` and panic if the converted value wouldn't fit
+   |
+LL |         Some(x) => { return x.try_into().unwrap() },
+   |                             ^^^^^^^^^^^^^^^^^^^^^
 
 error[E0308]: mismatched types
   --> $DIR/span-preservation.rs:33:22
diff --git a/src/test/ui/tail-typeck.stderr b/src/test/ui/tail-typeck.stderr
index eeeb258da21..69f8ffa581b 100644
--- a/src/test/ui/tail-typeck.stderr
+++ b/src/test/ui/tail-typeck.stderr
@@ -5,6 +5,11 @@ LL | fn f() -> isize { return g(); }
    |           -----          ^^^ expected `isize`, found `usize`
    |           |
    |           expected `isize` because of return type
+   |
+help: you can convert an `usize` to `isize` and panic if the converted value wouldn't fit
+   |
+LL | fn f() -> isize { return g().try_into().unwrap(); }
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/wrong-ret-type.stderr b/src/test/ui/wrong-ret-type.stderr
index 440620f40b1..c1274bd0ea6 100644
--- a/src/test/ui/wrong-ret-type.stderr
+++ b/src/test/ui/wrong-ret-type.stderr
@@ -5,6 +5,11 @@ LL | fn mk_int() -> usize { let i: isize = 3; return i; }
    |                -----                            ^ expected `usize`, found `isize`
    |                |
    |                expected `usize` because of return type
+   |
+help: you can convert an `isize` to `usize` and panic if the converted value wouldn't fit
+   |
+LL | fn mk_int() -> usize { let i: isize = 3; return i.try_into().unwrap(); }
+   |                                                 ^^^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to previous error