Skip to content

Commit 7fc1685

Browse files
committed
Tweak output of type mismatch between "then" and else if arms
1 parent 9aee7ed commit 7fc1685

File tree

10 files changed

+238
-13
lines changed

10 files changed

+238
-13
lines changed

src/librustc/infer/error_reporting/mod.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,10 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
511511
}
512512
}
513513
},
514+
ObligationCauseCode::IfExpression { then, outer } => {
515+
err.span_label(then, "expected because of this");
516+
outer.map(|sp| err.span_label(sp, "if and else have incompatible types"));
517+
}
514518
_ => (),
515519
}
516520
}
@@ -1460,7 +1464,7 @@ impl<'tcx> ObligationCause<'tcx> {
14601464
}
14611465
_ => "match arms have incompatible types",
14621466
}),
1463-
IfExpression => Error0308("if and else have incompatible types"),
1467+
IfExpression { .. } => Error0308("if and else have incompatible types"),
14641468
IfExpressionWithNoElse => Error0317("if may be missing an else clause"),
14651469
MainFunctionType => Error0580("main function has wrong type"),
14661470
StartFunctionType => Error0308("start function has wrong type"),
@@ -1488,7 +1492,7 @@ impl<'tcx> ObligationCause<'tcx> {
14881492
hir::MatchSource::IfLetDesugar { .. } => "`if let` arms have compatible types",
14891493
_ => "match arms have compatible types",
14901494
},
1491-
IfExpression => "if and else have compatible types",
1495+
IfExpression { .. } => "if and else have compatible types",
14921496
IfExpressionWithNoElse => "if missing an else returns ()",
14931497
MainFunctionType => "`main` function has the correct type",
14941498
StartFunctionType => "`start` function has the correct type",

src/librustc/traits/error_reporting.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1445,7 +1445,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
14451445
ObligationCauseCode::ExprAssignable |
14461446
ObligationCauseCode::MatchExpressionArm { .. } |
14471447
ObligationCauseCode::MatchExpressionArmPattern { .. } |
1448-
ObligationCauseCode::IfExpression |
1448+
ObligationCauseCode::IfExpression { .. } |
14491449
ObligationCauseCode::IfExpressionWithNoElse |
14501450
ObligationCauseCode::MainFunctionType |
14511451
ObligationCauseCode::StartFunctionType |

src/librustc/traits/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,10 @@ pub enum ObligationCauseCode<'tcx> {
229229
MatchExpressionArmPattern { span: Span, ty: Ty<'tcx> },
230230

231231
/// Computing common supertype in an if expression
232-
IfExpression,
232+
IfExpression {
233+
then: Span,
234+
outer: Option<Span>,
235+
},
233236

234237
/// Computing common supertype of an if expression with no else counter-part
235238
IfExpressionWithNoElse,

src/librustc/traits/structural_impls.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -520,7 +520,7 @@ impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> {
520520
super::MatchExpressionArmPattern { span, ty } => {
521521
tcx.lift(&ty).map(|ty| super::MatchExpressionArmPattern { span, ty })
522522
}
523-
super::IfExpression => Some(super::IfExpression),
523+
super::IfExpression { then, outer } => Some(super::IfExpression { then, outer }),
524524
super::IfExpressionWithNoElse => Some(super::IfExpressionWithNoElse),
525525
super::MainFunctionType => Some(super::MainFunctionType),
526526
super::StartFunctionType => Some(super::StartFunctionType),

src/librustc_typeck/check/mod.rs

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3366,7 +3366,93 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
33663366
let coerce_to_ty = expected.coercion_target_type(self, sp);
33673367
let mut coerce: DynamicCoerceMany = CoerceMany::new(coerce_to_ty);
33683368

3369-
let if_cause = self.cause(sp, ObligationCauseCode::IfExpression);
3369+
let mut outer_sp = if self.tcx.sess.source_map().is_multiline(sp) {
3370+
// The `if`/`else` isn't in one line in the output, include some context to make it
3371+
// clear it is an if/else expression:
3372+
// ```
3373+
// LL | let x = if true {
3374+
// | _____________-
3375+
// LL || 10i32
3376+
// || ----- expected because of this
3377+
// LL || } else {
3378+
// LL || 10u32
3379+
// || ^^^^^ expected i32, found u32
3380+
// LL || };
3381+
// ||_____- if and else have incompatible types
3382+
// ```
3383+
Some(sp)
3384+
} else {
3385+
// The entire expression is in one line, only point at the arms
3386+
// ```
3387+
// LL | let x = if true { 10i32 } else { 10u32 };
3388+
// | ----- ^^^^^ expected i32, found u32
3389+
// | |
3390+
// | expected because of this
3391+
// ```
3392+
None
3393+
};
3394+
let error_sp = opt_else_expr.map(|expr| {
3395+
if let ExprKind::Block(block, _) = &expr.node {
3396+
if let Some(expr) = &block.expr {
3397+
expr.span
3398+
} else if let Some(stmt) = block.stmts.last() {
3399+
// possibly incorrect trailing `;` in the else arm
3400+
stmt.span
3401+
} else { // empty block, point at its entirety
3402+
// Avoid overlapping spans that aren't as readable:
3403+
// ```
3404+
// 2 | let x = if true {
3405+
// | _____________-
3406+
// 3 | | 3
3407+
// | | - expected because of this
3408+
// 4 | | } else {
3409+
// | |____________^
3410+
// 5 | ||
3411+
// 6 | || };
3412+
// | || ^
3413+
// | ||_____|
3414+
// | |______if and else have incompatible types
3415+
// | expected integer, found ()
3416+
// ```
3417+
// by not pointing at the entire expression:
3418+
// ```
3419+
// 2 | let x = if true {
3420+
// | ------- if and else have incompatible types
3421+
// 3 | 3
3422+
// | - expected because of this
3423+
// 4 | } else {
3424+
// | ____________^
3425+
// 5 | |
3426+
// 6 | | };
3427+
// | |_____^ expected integer, found ()
3428+
// ```
3429+
if outer_sp.is_some() {
3430+
outer_sp = Some(self.tcx.sess.source_map().def_span(sp));
3431+
}
3432+
expr.span
3433+
}
3434+
} else { // shouldn't happen unless the parser has done something weird
3435+
expr.span
3436+
}
3437+
}).unwrap_or(sp); // shouldn't be needed
3438+
let then_sp = if let ExprKind::Block(block, _) = &then_expr.node {
3439+
if let Some(expr) = &block.expr {
3440+
expr.span
3441+
} else if let Some(stmt) = block.stmts.last() {
3442+
// possibly incorrect trailing `;` in the else arm
3443+
stmt.span
3444+
} else { // empty block, point at its entirety
3445+
outer_sp = None; // same as in `error_sp`, cleanup output
3446+
then_expr.span
3447+
}
3448+
} else { // shouldn't happen unless the parser has done something weird
3449+
then_expr.span
3450+
};
3451+
3452+
let if_cause = self.cause(error_sp, ObligationCauseCode::IfExpression {
3453+
then: then_sp,
3454+
outer: outer_sp,
3455+
});
33703456
coerce.coerce(self, &if_cause, then_expr, then_ty);
33713457

33723458
if let Some(else_expr) = opt_else_expr {

src/test/ui/if-else-type-mismatch.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
fn main() {
2+
let _ = if true {
3+
42i32
4+
} else {
5+
42u32
6+
};
7+
//~^^ ERROR if and else have incompatible types
8+
let _ = if true { 42i32 } else { 42u32 };
9+
//~^ ERROR if and else have incompatible types
10+
let _ = if true {
11+
42i32;
12+
} else {
13+
42u32
14+
};
15+
//~^^ ERROR if and else have incompatible types
16+
let _ = if true {
17+
42i32
18+
} else {
19+
42u32;
20+
};
21+
//~^^ ERROR if and else have incompatible types
22+
let _ = if true {
23+
24+
} else {
25+
42u32
26+
};
27+
//~^^ ERROR if and else have incompatible types
28+
let _ = if true {
29+
42i32
30+
} else {
31+
32+
};
33+
//~^^^ ERROR if and else have incompatible types
34+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
error[E0308]: if and else have incompatible types
2+
--> $DIR/if-else-type-mismatch.rs:5:9
3+
|
4+
LL | let _ = if true {
5+
| _____________-
6+
LL | | 42i32
7+
| | ----- expected because of this
8+
LL | | } else {
9+
LL | | 42u32
10+
| | ^^^^^ expected i32, found u32
11+
LL | | };
12+
| |_____- if and else have incompatible types
13+
|
14+
= note: expected type `i32`
15+
found type `u32`
16+
17+
error[E0308]: if and else have incompatible types
18+
--> $DIR/if-else-type-mismatch.rs:8:38
19+
|
20+
LL | let _ = if true { 42i32 } else { 42u32 };
21+
| ----- ^^^^^ expected i32, found u32
22+
| |
23+
| expected because of this
24+
|
25+
= note: expected type `i32`
26+
found type `u32`
27+
28+
error[E0308]: if and else have incompatible types
29+
--> $DIR/if-else-type-mismatch.rs:13:9
30+
|
31+
LL | let _ = if true {
32+
| _____________-
33+
LL | | 42i32;
34+
| | ------ expected because of this
35+
LL | | } else {
36+
LL | | 42u32
37+
| | ^^^^^ expected (), found u32
38+
LL | | };
39+
| |_____- if and else have incompatible types
40+
|
41+
= note: expected type `()`
42+
found type `u32`
43+
44+
error[E0308]: if and else have incompatible types
45+
--> $DIR/if-else-type-mismatch.rs:19:9
46+
|
47+
LL | let _ = if true {
48+
| _____________-
49+
LL | | 42i32
50+
| | ----- expected because of this
51+
LL | | } else {
52+
LL | | 42u32;
53+
| | ^^^^^^ expected i32, found ()
54+
LL | | };
55+
| |_____- if and else have incompatible types
56+
|
57+
= note: expected type `i32`
58+
found type `()`
59+
60+
error[E0308]: if and else have incompatible types
61+
--> $DIR/if-else-type-mismatch.rs:25:9
62+
|
63+
LL | let _ = if true {
64+
| _____________________-
65+
LL | |
66+
LL | | } else {
67+
| |_____- expected because of this
68+
LL | 42u32
69+
| ^^^^^ expected (), found u32
70+
|
71+
= note: expected type `()`
72+
found type `u32`
73+
74+
error[E0308]: if and else have incompatible types
75+
--> $DIR/if-else-type-mismatch.rs:30:12
76+
|
77+
LL | let _ = if true {
78+
| ------- if and else have incompatible types
79+
LL | 42i32
80+
| ----- expected because of this
81+
LL | } else {
82+
| ____________^
83+
LL | |
84+
LL | | };
85+
| |_____^ expected i32, found ()
86+
|
87+
= note: expected type `i32`
88+
found type `()`
89+
90+
error: aborting due to 6 previous errors
91+
92+
For more information about this error, try `rustc --explain E0308`.

src/test/ui/if/if-branch-types.stderr

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
error[E0308]: if and else have incompatible types
2-
--> $DIR/if-branch-types.rs:2:13
2+
--> $DIR/if-branch-types.rs:2:38
33
|
44
LL | let x = if true { 10i32 } else { 10u32 };
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected i32, found u32
5+
| ----- ^^^^^ expected i32, found u32
6+
| |
7+
| expected because of this
68
|
79
= note: expected type `i32`
810
found type `u32`

src/test/ui/regions/region-invariant-static-error-reporting.stderr

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
error[E0308]: if and else have incompatible types
2-
--> $DIR/region-invariant-static-error-reporting.rs:14:15
2+
--> $DIR/region-invariant-static-error-reporting.rs:17:9
33
|
44
LL | let bad = if x.is_some() {
5-
| _______________^
5+
| _______________-
66
LL | | x.unwrap()
7+
| | ---------- expected because of this
78
LL | | } else {
89
LL | | mk_static()
10+
| | ^^^^^^^^^^^ lifetime mismatch
911
LL | | };
10-
| |_____^ lifetime mismatch
12+
| |_____- if and else have incompatible types
1113
|
1214
= note: expected type `Invariant<'a>`
1315
found type `Invariant<'static>`

src/test/ui/str/str-array-assignment.stderr

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
error[E0308]: if and else have incompatible types
2-
--> $DIR/str-array-assignment.rs:3:11
2+
--> $DIR/str-array-assignment.rs:3:37
33
|
44
LL | let t = if true { s[..2] } else { s };
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected str, found &str
5+
| ------ ^ expected str, found &str
6+
| |
7+
| expected because of this
68
|
79
= note: expected type `str`
810
found type `&str`

0 commit comments

Comments
 (0)