Skip to content

Commit

Permalink
Emit coercion suggestions in more places
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Aaron1011 committed Dec 4, 2019
1 parent 7afe6d9 commit 462f06d
Show file tree
Hide file tree
Showing 13 changed files with 112 additions and 19 deletions.
4 changes: 4 additions & 0 deletions src/librustc_typeck/check/coercion.rs
Expand Up @@ -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());
Expand Down
22 changes: 17 additions & 5 deletions src/librustc_typeck/check/demand.rs
Expand Up @@ -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>) {
Expand Down Expand Up @@ -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))
}
Expand Down
8 changes: 6 additions & 2 deletions src/librustc_typeck/check/mod.rs
Expand Up @@ -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
}

Expand Down Expand Up @@ -4957,15 +4955,21 @@ 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,
"consider using `.await` here",
format!("{}.await", code),
Applicability::MaybeIncorrect,
);
} else {
debug!("suggest_missing_await: no snippet for {:?}", sp);
}
} else {
debug!("suggest_missing_await: obligation did not hold: {:?}", obligation)
}
}
}
Expand Down
11 changes: 11 additions & 0 deletions src/test/ui/async-await/suggest-missing-await.fixed
Expand Up @@ -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() {}
11 changes: 11 additions & 0 deletions src/test/ui/async-await/suggest-missing-await.rs
Expand Up @@ -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() {}
19 changes: 18 additions & 1 deletion src/test/ui/async-await/suggest-missing-await.stderr
Expand Up @@ -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`.
Expand Up @@ -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`
Expand Down
Expand Up @@ -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

Expand Down
13 changes: 9 additions & 4 deletions src/test/ui/issues/issue-59756.stderr
Expand Up @@ -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

Expand Down
10 changes: 8 additions & 2 deletions src/test/ui/mismatched_types/abridged.stderr
Expand Up @@ -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`
Expand All @@ -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`
Expand Down
5 changes: 5 additions & 0 deletions src/test/ui/proc-macro/span-preservation.stderr
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions src/test/ui/tail-typeck.stderr
Expand Up @@ -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

Expand Down
5 changes: 5 additions & 0 deletions src/test/ui/wrong-ret-type.stderr
Expand Up @@ -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

Expand Down

0 comments on commit 462f06d

Please sign in to comment.