Skip to content

Commit 9e934e2

Browse files
committed
Reweork incompatible match arms error
- Point at the body expression of the match arm with the type error. - Point at the prior match arms explicitely stating the evaluated type. - Point at the entire match expr in a secondary span, instead of primary. - For type errors in the first match arm, the cause is outside of the match, treat as implicit block error to give a more appropriate error.
1 parent 825f355 commit 9e934e2

File tree

12 files changed

+92
-62
lines changed

12 files changed

+92
-62
lines changed

src/librustc/infer/error_reporting/mod.rs

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -509,22 +509,27 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
509509
}
510510
}
511511
}
512-
ObligationCauseCode::MatchExpressionArm { arm_span, source } => match source {
512+
ObligationCauseCode::MatchExpressionArm {
513+
source,
514+
ref prior_arms,
515+
..
516+
} => match source {
513517
hir::MatchSource::IfLetDesugar { .. } => {
514-
let msg = "`if let` arm with an incompatible type";
515-
if self.tcx.sess.source_map().is_multiline(arm_span) {
516-
err.span_note(arm_span, msg);
517-
} else {
518-
err.span_label(arm_span, msg);
519-
}
518+
let msg = "`if let` arms have incompatible types";
519+
err.span_label(cause.span, msg);
520520
}
521521
hir::MatchSource::TryDesugar => {}
522522
_ => {
523-
let msg = "match arm with an incompatible type";
524-
if self.tcx.sess.source_map().is_multiline(arm_span) {
525-
err.span_note(arm_span, msg);
526-
} else {
527-
err.span_label(arm_span, msg);
523+
let msg = "`match` arms have incompatible types";
524+
err.span_label(cause.span, msg);
525+
if prior_arms.len() < 4 {
526+
for (sp, ty) in prior_arms {
527+
err.span_label(*sp, format!("this is found to be of type `{}`", ty));
528+
}
529+
} else if let Some((sp, ty)) = prior_arms.last() {
530+
err.span_label(*sp, format!(
531+
"this and all prior arms are found to be of type `{}`", ty,
532+
));
528533
}
529534
}
530535
},

src/librustc/traits/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ impl<'tcx> ObligationCause<'tcx> {
145145
ObligationCauseCode::StartFunctionType => {
146146
tcx.sess.source_map().def_span(self.span)
147147
}
148+
ObligationCauseCode::MatchExpressionArm { arm_span, .. } => arm_span,
148149
_ => self.span,
149150
}
150151
}
@@ -223,6 +224,7 @@ pub enum ObligationCauseCode<'tcx> {
223224
MatchExpressionArm {
224225
arm_span: Span,
225226
source: hir::MatchSource,
227+
prior_arms: Vec<(Span, Ty<'tcx>)>,
226228
},
227229

228230
/// Computing common supertype in the pattern guard for the arms of a match expression

src/librustc/traits/structural_impls.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -513,10 +513,16 @@ impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> {
513513
trait_item_def_id,
514514
}),
515515
super::ExprAssignable => Some(super::ExprAssignable),
516-
super::MatchExpressionArm { arm_span, source } => Some(super::MatchExpressionArm {
516+
super::MatchExpressionArm {
517517
arm_span,
518-
source: source,
519-
}),
518+
source,
519+
ref prior_arms,
520+
} => {
521+
let prior_arms = prior_arms.iter().filter_map(|(sp, ty)| {
522+
tcx.lift(ty).map(|ty| (*sp, ty))
523+
}).collect();
524+
Some(super::MatchExpressionArm { arm_span, source, prior_arms })
525+
}
520526
super::MatchExpressionArmPattern { span, ty } => {
521527
tcx.lift(&ty).map(|ty| super::MatchExpressionArmPattern { span, ty })
522528
}

src/librustc_typeck/check/_match.rs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -689,6 +689,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
689689
CoerceMany::with_coercion_sites(coerce_first, arms)
690690
};
691691

692+
let mut other_arms = vec![]; // used only for diagnostics
692693
for (i, (arm, pats_diverge)) in arms.iter().zip(all_arm_pats_diverge).enumerate() {
693694
if let Some(ref g) = arm.guard {
694695
self.diverges.set(pats_diverge);
@@ -709,17 +710,31 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
709710
_ => false
710711
};
711712

713+
let arm_span = if let hir::ExprKind::Block(ref blk, _) = arm.body.node {
714+
// Point at the block expr instead of the entire block
715+
blk.expr.as_ref().map(|e| e.span).unwrap_or(arm.body.span)
716+
} else {
717+
arm.body.span
718+
};
712719
if is_if_let_fallback {
713720
let cause = self.cause(expr.span, ObligationCauseCode::IfExpressionWithNoElse);
714721
assert!(arm_ty.is_unit());
715722
coercion.coerce_forced_unit(self, &cause, &mut |_| (), true);
716723
} else {
717-
let cause = self.cause(expr.span, ObligationCauseCode::MatchExpressionArm {
718-
arm_span: arm.body.span,
719-
source: match_src
720-
});
724+
let cause = if i == 0 {
725+
// The reason for the first arm to fail is not that the match arms diverge,
726+
// but rather that there's a prior obligation that doesn't hold.
727+
self.cause(arm_span, ObligationCauseCode::BlockTailExpression(arm.body.id))
728+
} else {
729+
self.cause(expr.span, ObligationCauseCode::MatchExpressionArm {
730+
arm_span,
731+
source: match_src,
732+
prior_arms: other_arms.clone(),
733+
})
734+
};
721735
coercion.coerce(self, &cause, &arm.body, arm_ty);
722736
}
737+
other_arms.push((arm_span, arm_ty));
723738
}
724739

725740
// We won't diverge unless the discriminant or all arms diverge.

src/test/ui/if/if-let-arm-types.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
fn main() {
2-
if let Some(b) = None { //~ ERROR: `if let` arms have incompatible types
3-
//~^ expected (), found integer
4-
//~| expected type `()`
5-
//~| found type `{integer}`
2+
if let Some(b) = None {
3+
//~^ NOTE if let` arms have incompatible types
64
()
75
} else {
86
1
97
};
8+
//~^^ ERROR: `if let` arms have incompatible types
9+
//~| NOTE expected (), found integer
10+
//~| NOTE expected type `()`
1011
}

src/test/ui/if/if-let-arm-types.stderr

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,17 @@
11
error[E0308]: `if let` arms have incompatible types
2-
--> $DIR/if-let-arm-types.rs:2:5
2+
--> $DIR/if-let-arm-types.rs:6:9
33
|
4-
LL | / if let Some(b) = None { //~ ERROR: `if let` arms have incompatible types
5-
LL | | //~^ expected (), found integer
6-
LL | | //~| expected type `()`
7-
LL | | //~| found type `{integer}`
8-
... |
4+
LL | / if let Some(b) = None {
5+
LL | | //~^ NOTE if let` arms have incompatible types
6+
LL | | ()
7+
LL | | } else {
98
LL | | 1
9+
| | ^ expected (), found integer
1010
LL | | };
11-
| |_____^ expected (), found integer
11+
| |_____- `if let` arms have incompatible types
1212
|
1313
= note: expected type `()`
1414
found type `{integer}`
15-
note: `if let` arm with an incompatible type
16-
--> $DIR/if-let-arm-types.rs:7:12
17-
|
18-
LL | } else {
19-
| ____________^
20-
LL | | 1
21-
LL | | };
22-
| |_____^
2315

2416
error: aborting due to previous error
2517

src/test/ui/issues/issue-11319.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
fn main() {
22
match Some(10) {
3-
//~^ ERROR match arms have incompatible types
4-
//~| expected type `bool`
5-
//~| found type `()`
6-
//~| expected bool, found ()
3+
//~^ NOTE `match` arms have incompatible types
74
Some(5) => false,
5+
//~^ NOTE this is found to be of type `bool`
86
Some(2) => true,
7+
//~^ NOTE this is found to be of type `bool`
98
None => (),
9+
//~^ ERROR match arms have incompatible types
10+
//~| NOTE expected bool, found ()
11+
//~| NOTE expected type `bool`
1012
_ => true
1113
}
1214
}

src/test/ui/issues/issue-11319.stderr

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
error[E0308]: match arms have incompatible types
2-
--> $DIR/issue-11319.rs:2:5
2+
--> $DIR/issue-11319.rs:8:20
33
|
44
LL | / match Some(10) {
5-
LL | | //~^ ERROR match arms have incompatible types
6-
LL | | //~| expected type `bool`
7-
LL | | //~| found type `()`
8-
... |
5+
LL | | //~^ NOTE `match` arms have incompatible types
6+
LL | | Some(5) => false,
7+
| | ----- this is found to be of type `bool`
8+
LL | | //~^ NOTE this is found to be of type `bool`
9+
LL | | Some(2) => true,
10+
| | ---- this is found to be of type `bool`
11+
LL | | //~^ NOTE this is found to be of type `bool`
912
LL | | None => (),
10-
| | -- match arm with an incompatible type
13+
| | ^^ expected bool, found ()
14+
... |
1115
LL | | _ => true
1216
LL | | }
13-
| |_____^ expected bool, found ()
17+
| |_____- `match` arms have incompatible types
1418
|
1519
= note: expected type `bool`
1620
found type `()`

src/test/ui/issues/issue-17728.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ impl Debug for Player {
9797
}
9898

9999
fn str_to_direction(to_parse: &str) -> RoomDirection {
100-
match to_parse { //~ ERROR match arms have incompatible types
100+
match to_parse {
101101
"w" | "west" => RoomDirection::West,
102102
"e" | "east" => RoomDirection::East,
103103
"n" | "north" => RoomDirection::North,
@@ -108,6 +108,7 @@ fn str_to_direction(to_parse: &str) -> RoomDirection {
108108
"down" => RoomDirection::Down,
109109
_ => None
110110
}
111+
//~^^ ERROR match arms have incompatible types
111112
}
112113

113114
fn main() {

src/test/ui/issues/issue-17728.stderr

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,19 @@ LL | Some(entry) => Ok(entry),
1010
| ^^^^^^^^^ ...but data from `room` is returned here
1111

1212
error[E0308]: match arms have incompatible types
13-
--> $DIR/issue-17728.rs:100:5
13+
--> $DIR/issue-17728.rs:109:14
1414
|
15-
LL | / match to_parse { //~ ERROR match arms have incompatible types
15+
LL | / match to_parse {
1616
LL | | "w" | "west" => RoomDirection::West,
1717
LL | | "e" | "east" => RoomDirection::East,
1818
LL | | "n" | "north" => RoomDirection::North,
1919
... |
20+
LL | | "down" => RoomDirection::Down,
21+
| | ------------------- this and all prior arms are found to be of type `RoomDirection`
2022
LL | | _ => None
21-
| | ---- match arm with an incompatible type
23+
| | ^^^^ expected enum `RoomDirection`, found enum `std::option::Option`
2224
LL | | }
23-
| |_____^ expected enum `RoomDirection`, found enum `std::option::Option`
25+
| |_____- `match` arms have incompatible types
2426
|
2527
= note: expected type `RoomDirection`
2628
found type `std::option::Option<_>`

src/test/ui/issues/issue-24036.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ fn closure_to_loc() {
66

77
fn closure_from_match() {
88
let x = match 1usize {
9-
//~^ ERROR match arms have incompatible types
109
1 => |c| c + 1,
1110
2 => |c| c - 1,
1211
_ => |c| c - 1
1312
};
13+
//~^^^ ERROR match arms have incompatible types
1414
}
1515

1616
fn main() { }

src/test/ui/issues/issue-24036.stderr

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,20 @@ LL | x = |c| c + 1;
1010
= help: consider boxing your closure and/or using it as a trait object
1111

1212
error[E0308]: match arms have incompatible types
13-
--> $DIR/issue-24036.rs:8:13
13+
--> $DIR/issue-24036.rs:10:14
1414
|
1515
LL | let x = match 1usize {
16-
| _____________^
17-
LL | | //~^ ERROR match arms have incompatible types
16+
| _____________-
1817
LL | | 1 => |c| c + 1,
18+
| | --------- this is found to be of type `[closure@$DIR/issue-24036.rs:9:14: 9:23]`
1919
LL | | 2 => |c| c - 1,
20-
| | --------- match arm with an incompatible type
20+
| | ^^^^^^^^^ expected closure, found a different closure
2121
LL | | _ => |c| c - 1
2222
LL | | };
23-
| |_____^ expected closure, found a different closure
23+
| |_____- `match` arms have incompatible types
2424
|
25-
= note: expected type `[closure@$DIR/issue-24036.rs:10:14: 10:23]`
26-
found type `[closure@$DIR/issue-24036.rs:11:14: 11:23]`
25+
= note: expected type `[closure@$DIR/issue-24036.rs:9:14: 9:23]`
26+
found type `[closure@$DIR/issue-24036.rs:10:14: 10:23]`
2727
= note: no two closures, even if identical, have the same type
2828
= help: consider boxing your closure and/or using it as a trait object
2929

0 commit comments

Comments
 (0)