Tweak output of type mismatch between "then" and else
if
arms
This commit is contained in:
parent
9aee7ed335
commit
7fc1685c47
10 changed files with 238 additions and 13 deletions
|
@ -511,6 +511,10 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
ObligationCauseCode::IfExpression { then, outer } => {
|
||||||
|
err.span_label(then, "expected because of this");
|
||||||
|
outer.map(|sp| err.span_label(sp, "if and else have incompatible types"));
|
||||||
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1460,7 +1464,7 @@ impl<'tcx> ObligationCause<'tcx> {
|
||||||
}
|
}
|
||||||
_ => "match arms have incompatible types",
|
_ => "match arms have incompatible types",
|
||||||
}),
|
}),
|
||||||
IfExpression => Error0308("if and else have incompatible types"),
|
IfExpression { .. } => Error0308("if and else have incompatible types"),
|
||||||
IfExpressionWithNoElse => Error0317("if may be missing an else clause"),
|
IfExpressionWithNoElse => Error0317("if may be missing an else clause"),
|
||||||
MainFunctionType => Error0580("main function has wrong type"),
|
MainFunctionType => Error0580("main function has wrong type"),
|
||||||
StartFunctionType => Error0308("start function has wrong type"),
|
StartFunctionType => Error0308("start function has wrong type"),
|
||||||
|
@ -1488,7 +1492,7 @@ impl<'tcx> ObligationCause<'tcx> {
|
||||||
hir::MatchSource::IfLetDesugar { .. } => "`if let` arms have compatible types",
|
hir::MatchSource::IfLetDesugar { .. } => "`if let` arms have compatible types",
|
||||||
_ => "match arms have compatible types",
|
_ => "match arms have compatible types",
|
||||||
},
|
},
|
||||||
IfExpression => "if and else have compatible types",
|
IfExpression { .. } => "if and else have compatible types",
|
||||||
IfExpressionWithNoElse => "if missing an else returns ()",
|
IfExpressionWithNoElse => "if missing an else returns ()",
|
||||||
MainFunctionType => "`main` function has the correct type",
|
MainFunctionType => "`main` function has the correct type",
|
||||||
StartFunctionType => "`start` function has the correct type",
|
StartFunctionType => "`start` function has the correct type",
|
||||||
|
|
|
@ -1445,7 +1445,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
||||||
ObligationCauseCode::ExprAssignable |
|
ObligationCauseCode::ExprAssignable |
|
||||||
ObligationCauseCode::MatchExpressionArm { .. } |
|
ObligationCauseCode::MatchExpressionArm { .. } |
|
||||||
ObligationCauseCode::MatchExpressionArmPattern { .. } |
|
ObligationCauseCode::MatchExpressionArmPattern { .. } |
|
||||||
ObligationCauseCode::IfExpression |
|
ObligationCauseCode::IfExpression { .. } |
|
||||||
ObligationCauseCode::IfExpressionWithNoElse |
|
ObligationCauseCode::IfExpressionWithNoElse |
|
||||||
ObligationCauseCode::MainFunctionType |
|
ObligationCauseCode::MainFunctionType |
|
||||||
ObligationCauseCode::StartFunctionType |
|
ObligationCauseCode::StartFunctionType |
|
||||||
|
|
|
@ -229,7 +229,10 @@ pub enum ObligationCauseCode<'tcx> {
|
||||||
MatchExpressionArmPattern { span: Span, ty: Ty<'tcx> },
|
MatchExpressionArmPattern { span: Span, ty: Ty<'tcx> },
|
||||||
|
|
||||||
/// Computing common supertype in an if expression
|
/// Computing common supertype in an if expression
|
||||||
IfExpression,
|
IfExpression {
|
||||||
|
then: Span,
|
||||||
|
outer: Option<Span>,
|
||||||
|
},
|
||||||
|
|
||||||
/// Computing common supertype of an if expression with no else counter-part
|
/// Computing common supertype of an if expression with no else counter-part
|
||||||
IfExpressionWithNoElse,
|
IfExpressionWithNoElse,
|
||||||
|
|
|
@ -520,7 +520,7 @@ impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> {
|
||||||
super::MatchExpressionArmPattern { span, ty } => {
|
super::MatchExpressionArmPattern { span, ty } => {
|
||||||
tcx.lift(&ty).map(|ty| super::MatchExpressionArmPattern { span, ty })
|
tcx.lift(&ty).map(|ty| super::MatchExpressionArmPattern { span, ty })
|
||||||
}
|
}
|
||||||
super::IfExpression => Some(super::IfExpression),
|
super::IfExpression { then, outer } => Some(super::IfExpression { then, outer }),
|
||||||
super::IfExpressionWithNoElse => Some(super::IfExpressionWithNoElse),
|
super::IfExpressionWithNoElse => Some(super::IfExpressionWithNoElse),
|
||||||
super::MainFunctionType => Some(super::MainFunctionType),
|
super::MainFunctionType => Some(super::MainFunctionType),
|
||||||
super::StartFunctionType => Some(super::StartFunctionType),
|
super::StartFunctionType => Some(super::StartFunctionType),
|
||||||
|
|
|
@ -3366,7 +3366,93 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||||
let coerce_to_ty = expected.coercion_target_type(self, sp);
|
let coerce_to_ty = expected.coercion_target_type(self, sp);
|
||||||
let mut coerce: DynamicCoerceMany = CoerceMany::new(coerce_to_ty);
|
let mut coerce: DynamicCoerceMany = CoerceMany::new(coerce_to_ty);
|
||||||
|
|
||||||
let if_cause = self.cause(sp, ObligationCauseCode::IfExpression);
|
let mut outer_sp = if self.tcx.sess.source_map().is_multiline(sp) {
|
||||||
|
// The `if`/`else` isn't in one line in the output, include some context to make it
|
||||||
|
// clear it is an if/else expression:
|
||||||
|
// ```
|
||||||
|
// LL | let x = if true {
|
||||||
|
// | _____________-
|
||||||
|
// LL || 10i32
|
||||||
|
// || ----- expected because of this
|
||||||
|
// LL || } else {
|
||||||
|
// LL || 10u32
|
||||||
|
// || ^^^^^ expected i32, found u32
|
||||||
|
// LL || };
|
||||||
|
// ||_____- if and else have incompatible types
|
||||||
|
// ```
|
||||||
|
Some(sp)
|
||||||
|
} else {
|
||||||
|
// The entire expression is in one line, only point at the arms
|
||||||
|
// ```
|
||||||
|
// LL | let x = if true { 10i32 } else { 10u32 };
|
||||||
|
// | ----- ^^^^^ expected i32, found u32
|
||||||
|
// | |
|
||||||
|
// | expected because of this
|
||||||
|
// ```
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let error_sp = opt_else_expr.map(|expr| {
|
||||||
|
if let ExprKind::Block(block, _) = &expr.node {
|
||||||
|
if let Some(expr) = &block.expr {
|
||||||
|
expr.span
|
||||||
|
} else if let Some(stmt) = block.stmts.last() {
|
||||||
|
// possibly incorrect trailing `;` in the else arm
|
||||||
|
stmt.span
|
||||||
|
} else { // empty block, point at its entirety
|
||||||
|
// Avoid overlapping spans that aren't as readable:
|
||||||
|
// ```
|
||||||
|
// 2 | let x = if true {
|
||||||
|
// | _____________-
|
||||||
|
// 3 | | 3
|
||||||
|
// | | - expected because of this
|
||||||
|
// 4 | | } else {
|
||||||
|
// | |____________^
|
||||||
|
// 5 | ||
|
||||||
|
// 6 | || };
|
||||||
|
// | || ^
|
||||||
|
// | ||_____|
|
||||||
|
// | |______if and else have incompatible types
|
||||||
|
// | expected integer, found ()
|
||||||
|
// ```
|
||||||
|
// by not pointing at the entire expression:
|
||||||
|
// ```
|
||||||
|
// 2 | let x = if true {
|
||||||
|
// | ------- if and else have incompatible types
|
||||||
|
// 3 | 3
|
||||||
|
// | - expected because of this
|
||||||
|
// 4 | } else {
|
||||||
|
// | ____________^
|
||||||
|
// 5 | |
|
||||||
|
// 6 | | };
|
||||||
|
// | |_____^ expected integer, found ()
|
||||||
|
// ```
|
||||||
|
if outer_sp.is_some() {
|
||||||
|
outer_sp = Some(self.tcx.sess.source_map().def_span(sp));
|
||||||
|
}
|
||||||
|
expr.span
|
||||||
|
}
|
||||||
|
} else { // shouldn't happen unless the parser has done something weird
|
||||||
|
expr.span
|
||||||
|
}
|
||||||
|
}).unwrap_or(sp); // shouldn't be needed
|
||||||
|
let then_sp = if let ExprKind::Block(block, _) = &then_expr.node {
|
||||||
|
if let Some(expr) = &block.expr {
|
||||||
|
expr.span
|
||||||
|
} else if let Some(stmt) = block.stmts.last() {
|
||||||
|
// possibly incorrect trailing `;` in the else arm
|
||||||
|
stmt.span
|
||||||
|
} else { // empty block, point at its entirety
|
||||||
|
outer_sp = None; // same as in `error_sp`, cleanup output
|
||||||
|
then_expr.span
|
||||||
|
}
|
||||||
|
} else { // shouldn't happen unless the parser has done something weird
|
||||||
|
then_expr.span
|
||||||
|
};
|
||||||
|
|
||||||
|
let if_cause = self.cause(error_sp, ObligationCauseCode::IfExpression {
|
||||||
|
then: then_sp,
|
||||||
|
outer: outer_sp,
|
||||||
|
});
|
||||||
coerce.coerce(self, &if_cause, then_expr, then_ty);
|
coerce.coerce(self, &if_cause, then_expr, then_ty);
|
||||||
|
|
||||||
if let Some(else_expr) = opt_else_expr {
|
if let Some(else_expr) = opt_else_expr {
|
||||||
|
|
34
src/test/ui/if-else-type-mismatch.rs
Normal file
34
src/test/ui/if-else-type-mismatch.rs
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
fn main() {
|
||||||
|
let _ = if true {
|
||||||
|
42i32
|
||||||
|
} else {
|
||||||
|
42u32
|
||||||
|
};
|
||||||
|
//~^^ ERROR if and else have incompatible types
|
||||||
|
let _ = if true { 42i32 } else { 42u32 };
|
||||||
|
//~^ ERROR if and else have incompatible types
|
||||||
|
let _ = if true {
|
||||||
|
42i32;
|
||||||
|
} else {
|
||||||
|
42u32
|
||||||
|
};
|
||||||
|
//~^^ ERROR if and else have incompatible types
|
||||||
|
let _ = if true {
|
||||||
|
42i32
|
||||||
|
} else {
|
||||||
|
42u32;
|
||||||
|
};
|
||||||
|
//~^^ ERROR if and else have incompatible types
|
||||||
|
let _ = if true {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
42u32
|
||||||
|
};
|
||||||
|
//~^^ ERROR if and else have incompatible types
|
||||||
|
let _ = if true {
|
||||||
|
42i32
|
||||||
|
} else {
|
||||||
|
|
||||||
|
};
|
||||||
|
//~^^^ ERROR if and else have incompatible types
|
||||||
|
}
|
92
src/test/ui/if-else-type-mismatch.stderr
Normal file
92
src/test/ui/if-else-type-mismatch.stderr
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
error[E0308]: if and else have incompatible types
|
||||||
|
--> $DIR/if-else-type-mismatch.rs:5:9
|
||||||
|
|
|
||||||
|
LL | let _ = if true {
|
||||||
|
| _____________-
|
||||||
|
LL | | 42i32
|
||||||
|
| | ----- expected because of this
|
||||||
|
LL | | } else {
|
||||||
|
LL | | 42u32
|
||||||
|
| | ^^^^^ expected i32, found u32
|
||||||
|
LL | | };
|
||||||
|
| |_____- if and else have incompatible types
|
||||||
|
|
|
||||||
|
= note: expected type `i32`
|
||||||
|
found type `u32`
|
||||||
|
|
||||||
|
error[E0308]: if and else have incompatible types
|
||||||
|
--> $DIR/if-else-type-mismatch.rs:8:38
|
||||||
|
|
|
||||||
|
LL | let _ = if true { 42i32 } else { 42u32 };
|
||||||
|
| ----- ^^^^^ expected i32, found u32
|
||||||
|
| |
|
||||||
|
| expected because of this
|
||||||
|
|
|
||||||
|
= note: expected type `i32`
|
||||||
|
found type `u32`
|
||||||
|
|
||||||
|
error[E0308]: if and else have incompatible types
|
||||||
|
--> $DIR/if-else-type-mismatch.rs:13:9
|
||||||
|
|
|
||||||
|
LL | let _ = if true {
|
||||||
|
| _____________-
|
||||||
|
LL | | 42i32;
|
||||||
|
| | ------ expected because of this
|
||||||
|
LL | | } else {
|
||||||
|
LL | | 42u32
|
||||||
|
| | ^^^^^ expected (), found u32
|
||||||
|
LL | | };
|
||||||
|
| |_____- if and else have incompatible types
|
||||||
|
|
|
||||||
|
= note: expected type `()`
|
||||||
|
found type `u32`
|
||||||
|
|
||||||
|
error[E0308]: if and else have incompatible types
|
||||||
|
--> $DIR/if-else-type-mismatch.rs:19:9
|
||||||
|
|
|
||||||
|
LL | let _ = if true {
|
||||||
|
| _____________-
|
||||||
|
LL | | 42i32
|
||||||
|
| | ----- expected because of this
|
||||||
|
LL | | } else {
|
||||||
|
LL | | 42u32;
|
||||||
|
| | ^^^^^^ expected i32, found ()
|
||||||
|
LL | | };
|
||||||
|
| |_____- if and else have incompatible types
|
||||||
|
|
|
||||||
|
= note: expected type `i32`
|
||||||
|
found type `()`
|
||||||
|
|
||||||
|
error[E0308]: if and else have incompatible types
|
||||||
|
--> $DIR/if-else-type-mismatch.rs:25:9
|
||||||
|
|
|
||||||
|
LL | let _ = if true {
|
||||||
|
| _____________________-
|
||||||
|
LL | |
|
||||||
|
LL | | } else {
|
||||||
|
| |_____- expected because of this
|
||||||
|
LL | 42u32
|
||||||
|
| ^^^^^ expected (), found u32
|
||||||
|
|
|
||||||
|
= note: expected type `()`
|
||||||
|
found type `u32`
|
||||||
|
|
||||||
|
error[E0308]: if and else have incompatible types
|
||||||
|
--> $DIR/if-else-type-mismatch.rs:30:12
|
||||||
|
|
|
||||||
|
LL | let _ = if true {
|
||||||
|
| ------- if and else have incompatible types
|
||||||
|
LL | 42i32
|
||||||
|
| ----- expected because of this
|
||||||
|
LL | } else {
|
||||||
|
| ____________^
|
||||||
|
LL | |
|
||||||
|
LL | | };
|
||||||
|
| |_____^ expected i32, found ()
|
||||||
|
|
|
||||||
|
= note: expected type `i32`
|
||||||
|
found type `()`
|
||||||
|
|
||||||
|
error: aborting due to 6 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0308`.
|
|
@ -1,8 +1,10 @@
|
||||||
error[E0308]: if and else have incompatible types
|
error[E0308]: if and else have incompatible types
|
||||||
--> $DIR/if-branch-types.rs:2:13
|
--> $DIR/if-branch-types.rs:2:38
|
||||||
|
|
|
|
||||||
LL | let x = if true { 10i32 } else { 10u32 };
|
LL | let x = if true { 10i32 } else { 10u32 };
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected i32, found u32
|
| ----- ^^^^^ expected i32, found u32
|
||||||
|
| |
|
||||||
|
| expected because of this
|
||||||
|
|
|
|
||||||
= note: expected type `i32`
|
= note: expected type `i32`
|
||||||
found type `u32`
|
found type `u32`
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
error[E0308]: if and else have incompatible types
|
error[E0308]: if and else have incompatible types
|
||||||
--> $DIR/region-invariant-static-error-reporting.rs:14:15
|
--> $DIR/region-invariant-static-error-reporting.rs:17:9
|
||||||
|
|
|
|
||||||
LL | let bad = if x.is_some() {
|
LL | let bad = if x.is_some() {
|
||||||
| _______________^
|
| _______________-
|
||||||
LL | | x.unwrap()
|
LL | | x.unwrap()
|
||||||
|
| | ---------- expected because of this
|
||||||
LL | | } else {
|
LL | | } else {
|
||||||
LL | | mk_static()
|
LL | | mk_static()
|
||||||
|
| | ^^^^^^^^^^^ lifetime mismatch
|
||||||
LL | | };
|
LL | | };
|
||||||
| |_____^ lifetime mismatch
|
| |_____- if and else have incompatible types
|
||||||
|
|
|
|
||||||
= note: expected type `Invariant<'a>`
|
= note: expected type `Invariant<'a>`
|
||||||
found type `Invariant<'static>`
|
found type `Invariant<'static>`
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
error[E0308]: if and else have incompatible types
|
error[E0308]: if and else have incompatible types
|
||||||
--> $DIR/str-array-assignment.rs:3:11
|
--> $DIR/str-array-assignment.rs:3:37
|
||||||
|
|
|
|
||||||
LL | let t = if true { s[..2] } else { s };
|
LL | let t = if true { s[..2] } else { s };
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected str, found &str
|
| ------ ^ expected str, found &str
|
||||||
|
| |
|
||||||
|
| expected because of this
|
||||||
|
|
|
|
||||||
= note: expected type `str`
|
= note: expected type `str`
|
||||||
found type `&str`
|
found type `&str`
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue