Skip to content

Commit

Permalink
Rollup merge of rust-lang#57723 - estebank:fix, r=davidtwco
Browse files Browse the repository at this point in the history
Point at cause for expectation in return type type error

Various improvements and fixes for type errors in return expressions.

Fix rust-lang#57664.
  • Loading branch information
Centril committed Jan 19, 2019
2 parents 3bc61cd + 2e06d9c commit 5b9e02a
Show file tree
Hide file tree
Showing 13 changed files with 201 additions and 19 deletions.
42 changes: 37 additions & 5 deletions src/librustc_typeck/check/coercion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1216,28 +1216,60 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E>
"supposed to be part of a block tail expression, but the \
expression is empty");
});
fcx.suggest_mismatched_types_on_tail(
let pointing_at_return_type = fcx.suggest_mismatched_types_on_tail(
&mut db,
expr,
expected,
found,
cause.span,
blk_id,
);
if let Some(sp) = fcx.ret_coercion_span.borrow().as_ref() {
if !sp.overlaps(cause.span) {
// FIXME: replace with navigating up the chain until hitting an fn or
// bailing if no "pass-through" Node is found, in order to provide a
// suggestion when encountering something like:
// ```
// fn foo(a: bool) -> impl Debug {
// if a {
// bar()?;
// }
// {
// let x = unsafe { bar() };
// x
// }
// }
// ```
//
// Verify that this is a tail expression of a function, otherwise the
// label pointing out the cause for the type coercion will be wrong
// as prior return coercions would not be relevant (#57664).
let parent_id = fcx.tcx.hir().get_parent_node(blk_id);
let parent = fcx.tcx.hir().get(fcx.tcx.hir().get_parent_node(parent_id));
if fcx.get_node_fn_decl(parent).is_some() && !pointing_at_return_type {
if let Some(sp) = fcx.ret_coercion_span.borrow().as_ref() {
db.span_label(*sp, reason_label);
}
}
}
_ => {
ObligationCauseCode::ReturnType(_id) => {
db = fcx.report_mismatched_types(cause, expected, found, err);
if let Some(sp) = fcx.ret_coercion_span.borrow().as_ref() {
let _id = fcx.tcx.hir().get_parent_node(_id);
let mut pointing_at_return_type = false;
if let Some((fn_decl, can_suggest)) = fcx.get_fn_decl(_id) {
pointing_at_return_type = fcx.suggest_missing_return_type(
&mut db, &fn_decl, expected, found, can_suggest);
}
if let (Some(sp), false) = (
fcx.ret_coercion_span.borrow().as_ref(),
pointing_at_return_type,
) {
if !sp.overlaps(cause.span) {
db.span_label(*sp, reason_label);
}
}
}
_ => {
db = fcx.report_mismatched_types(cause, expected, found, err);
}
}

if let Some(augment_error) = augment_error {
Expand Down
36 changes: 25 additions & 11 deletions src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4347,11 +4347,15 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
struct_span_err!(self.tcx.sess, expr.span, E0572,
"return statement outside of function body").emit();
} else if let Some(ref e) = *expr_opt {
*self.ret_coercion_span.borrow_mut() = Some(e.span);
if self.ret_coercion_span.borrow().is_none() {
*self.ret_coercion_span.borrow_mut() = Some(e.span);
}
self.check_return_expr(e);
} else {
let mut coercion = self.ret_coercion.as_ref().unwrap().borrow_mut();
*self.ret_coercion_span.borrow_mut() = Some(expr.span);
if self.ret_coercion_span.borrow().is_none() {
*self.ret_coercion_span.borrow_mut() = Some(expr.span);
}
let cause = self.cause(expr.span, ObligationCauseCode::ReturnNoExpression);
if let Some((fn_decl, _)) = self.get_fn_decl(expr.id) {
coercion.coerce_forced_unit(
Expand Down Expand Up @@ -5081,12 +5085,15 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
found: Ty<'tcx>,
cause_span: Span,
blk_id: ast::NodeId,
) {
) -> bool {
self.suggest_missing_semicolon(err, expression, expected, cause_span);
let mut pointing_at_return_type = false;
if let Some((fn_decl, can_suggest)) = self.get_fn_decl(blk_id) {
self.suggest_missing_return_type(err, &fn_decl, expected, found, can_suggest);
pointing_at_return_type = self.suggest_missing_return_type(
err, &fn_decl, expected, found, can_suggest);
}
self.suggest_ref_or_into(err, expression, expected, found);
pointing_at_return_type
}

pub fn suggest_ref_or_into(
Expand Down Expand Up @@ -5185,12 +5192,14 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
/// This routine checks if the return type is left as default, the method is not part of an
/// `impl` block and that it isn't the `main` method. If so, it suggests setting the return
/// type.
fn suggest_missing_return_type(&self,
err: &mut DiagnosticBuilder<'tcx>,
fn_decl: &hir::FnDecl,
expected: Ty<'tcx>,
found: Ty<'tcx>,
can_suggest: bool) {
fn suggest_missing_return_type(
&self,
err: &mut DiagnosticBuilder<'tcx>,
fn_decl: &hir::FnDecl,
expected: Ty<'tcx>,
found: Ty<'tcx>,
can_suggest: bool,
) -> bool {
// Only suggest changing the return type for methods that
// haven't set a return type at all (and aren't `fn main()` or an impl).
match (&fn_decl.output, found.is_suggestable(), can_suggest, expected.is_unit()) {
Expand All @@ -5200,16 +5209,19 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
"try adding a return type",
format!("-> {} ", self.resolve_type_vars_with_obligations(found)),
Applicability::MachineApplicable);
true
}
(&hir::FunctionRetTy::DefaultReturn(span), false, true, true) => {
err.span_label(span, "possibly return type missing here?");
true
}
(&hir::FunctionRetTy::DefaultReturn(span), _, false, true) => {
// `fn main()` must return `()`, do not suggest changing return type
err.span_label(span, "expected `()` because of default return type");
true
}
// expectation was caused by something else, not the default return
(&hir::FunctionRetTy::DefaultReturn(_), _, _, false) => {}
(&hir::FunctionRetTy::DefaultReturn(_), _, _, false) => false,
(&hir::FunctionRetTy::Return(ref ty), _, _, _) => {
// Only point to return type if the expected type is the return type, as if they
// are not, the expectation must have been caused by something else.
Expand All @@ -5221,7 +5233,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
if ty.sty == expected.sty {
err.span_label(sp, format!("expected `{}` because of return type",
expected));
return true;
}
false
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
error[E0308]: mismatched types
--> $DIR/fully-qualified-type-name2.rs:12:12
|
LL | fn bar(x: x::Foo) -> y::Foo {
| ------ expected `y::Foo` because of return type
LL | return x;
| ^ expected enum `y::Foo`, found enum `x::Foo`
|
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
error[E0308]: mismatched types
--> $DIR/fully-qualified-type-name4.rs:6:12
|
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
|
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/liveness/liveness-forgot-ret.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ error[E0308]: mismatched types
--> $DIR/liveness-forgot-ret.rs:3:19
|
LL | fn f(a: isize) -> isize { if god_exists(a) { return 5; }; }
| - ^^^^^ expected isize, found () - expected because of this statement
| - ^^^^^ expected isize, found ()
| |
| this function's body doesn't return
|
Expand Down
17 changes: 17 additions & 0 deletions src/test/ui/point-to-type-err-cause-on-impl-trait-return-2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
fn unrelated() -> Result<(), std::string::ParseError> { // #57664
let x = 0;

match x {
1 => {
let property_value_as_string = "a".parse()?;
}
2 => {
let value: &bool = unsafe { &42 };
//~^ ERROR mismatched types
}
};

Ok(())
}

fn main() {}
12 changes: 12 additions & 0 deletions src/test/ui/point-to-type-err-cause-on-impl-trait-return-2.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
error[E0308]: mismatched types
--> $DIR/point-to-type-err-cause-on-impl-trait-return-2.rs:9:41
|
LL | let value: &bool = unsafe { &42 };
| ^^^ expected bool, found integer
|
= note: expected type `&bool`
found type `&{integer}`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.
36 changes: 36 additions & 0 deletions src/test/ui/point-to-type-err-cause-on-impl-trait-return.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
fn foo() -> impl std::fmt::Display {
if false {
return 0i32;
}
1u32
//~^ ERROR mismatched types
}

fn bar() -> impl std::fmt::Display {
if false {
return 0i32;
} else {
return 1u32;
//~^ ERROR mismatched types
}
}

fn baz() -> impl std::fmt::Display {
if false {
//~^ ERROR mismatched types
return 0i32;
} else {
1u32
}
}

fn qux() -> impl std::fmt::Display {
if false {
0i32
} else {
1u32
//~^ ERROR if and else have incompatible types
}
}

fn main() {}
58 changes: 58 additions & 0 deletions src/test/ui/point-to-type-err-cause-on-impl-trait-return.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
error[E0308]: mismatched types
--> $DIR/point-to-type-err-cause-on-impl-trait-return.rs:5:5
|
LL | return 0i32;
| ---- expected because of this statement
LL | }
LL | 1u32
| ^^^^ expected i32, found u32
|
= note: expected type `i32`
found type `u32`

error[E0308]: mismatched types
--> $DIR/point-to-type-err-cause-on-impl-trait-return.rs:13:16
|
LL | return 0i32;
| ---- expected because of this statement
LL | } else {
LL | return 1u32;
| ^^^^ expected i32, found u32
|
= note: expected type `i32`
found type `u32`

error[E0308]: mismatched types
--> $DIR/point-to-type-err-cause-on-impl-trait-return.rs:19:5
|
LL | / if false {
LL | | //~^ ERROR mismatched types
LL | | return 0i32;
| | ---- expected because of this statement
LL | | } else {
LL | | 1u32
LL | | }
| |_____^ expected i32, found u32
|
= note: expected type `i32`
found type `u32`

error[E0308]: if and else have incompatible types
--> $DIR/point-to-type-err-cause-on-impl-trait-return.rs:31:9
|
LL | / if false {
LL | | 0i32
| | ---- expected because of this
LL | | } else {
LL | | 1u32
| | ^^^^ expected i32, found u32
LL | | //~^ ERROR if and else have incompatible types
LL | | }
| |_____- if and else have incompatible types
|
= note: expected type `i32`
found type `u32`

error: aborting due to 4 previous errors

For more information about this error, try `rustc --explain E0308`.
3 changes: 3 additions & 0 deletions src/test/ui/proc-macro/span-preservation.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ LL | let x: usize = "hello";;;;; //~ ERROR mismatched types
error[E0308]: mismatched types
--> $DIR/span-preservation.rs:19:29
|
LL | fn b(x: Option<isize>) -> usize {
| ----- expected `usize` because of return type
LL | match x {
LL | Some(x) => { return x }, //~ ERROR mismatched types
| ^ expected usize, found isize

Expand Down
2 changes: 2 additions & 0 deletions src/test/ui/return/return-from-diverging.stderr
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
error[E0308]: mismatched types
--> $DIR/return-from-diverging.rs:4:12
|
LL | fn fail() -> ! {
| - expected `!` because of return type
LL | return "wow"; //~ ERROR mismatched types
| ^^^^^ expected !, found reference
|
Expand Down
4 changes: 3 additions & 1 deletion src/test/ui/tail-typeck.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ error[E0308]: mismatched types
--> $DIR/tail-typeck.rs:3:26
|
LL | fn f() -> isize { return g(); }
| ^^^ expected isize, found usize
| ----- ^^^ expected isize, found usize
| |
| expected `isize` because of return type

error: aborting due to previous error

Expand Down
4 changes: 3 additions & 1 deletion src/test/ui/wrong-ret-type.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ error[E0308]: mismatched types
--> $DIR/wrong-ret-type.rs:2:49
|
LL | fn mk_int() -> usize { let i: isize = 3; return i; }
| ^ expected usize, found isize
| ----- ^ expected usize, found isize
| |
| expected `usize` because of return type

error: aborting due to previous error

Expand Down

0 comments on commit 5b9e02a

Please sign in to comment.