Rollup merge of #57723 - estebank:fix, r=davidtwco
Point at cause for expectation in return type type error Various improvements and fixes for type errors in return expressions. Fix #57664.
This commit is contained in:
commit
5b9e02a39c
13 changed files with 203 additions and 21 deletions
|
|
@ -1216,7 +1216,7 @@ 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,
|
||||
|
|
@ -1224,7 +1224,44 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E>
|
|||
cause.span,
|
||||
blk_id,
|
||||
);
|
||||
if let Some(sp) = fcx.ret_coercion_span.borrow().as_ref() {
|
||||
// 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);
|
||||
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);
|
||||
}
|
||||
|
|
@ -1232,11 +1269,6 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E>
|
|||
}
|
||||
_ => {
|
||||
db = fcx.report_mismatched_types(cause, expected, found, err);
|
||||
if let Some(sp) = fcx.ret_coercion_span.borrow().as_ref() {
|
||||
if !sp.overlaps(cause.span) {
|
||||
db.span_label(*sp, reason_label);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
@ -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(
|
||||
|
|
@ -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()) {
|
||||
|
|
@ -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.
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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`
|
||||
|
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
||||
|
|
|
|||
|
|
@ -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() {}
|
||||
|
|
@ -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
src/test/ui/point-to-type-err-cause-on-impl-trait-return.rs
Normal file
36
src/test/ui/point-to-type-err-cause-on-impl-trait-return.rs
Normal file
|
|
@ -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() {}
|
||||
|
|
@ -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`.
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue