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.
This commit is contained in:
Aaron Hill 2019-12-03 20:32:50 -05:00
parent 7afe6d9d1f
commit 462f06de07
No known key found for this signature in database
GPG Key ID: B4087E510E98B164
13 changed files with 112 additions and 19 deletions

View File

@ -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());

View File

@ -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>) {
@ -127,11 +143,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))
}

View File

@ -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)
}
}
}

View File

@ -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() {}

View File

@ -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() {}

View File

@ -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`.

View File

@ -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`

View File

@ -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

View File

@ -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

View File

@ -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`

View File

@ -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

View File

@ -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

View File

@ -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