Skip to content

Commit

Permalink
Rollup merge of rust-lang#58267 - estebank:match-arms, r=matthewjasper
Browse files Browse the repository at this point in the history
Tweak "incompatible match arms" error

- Point at the body expression of the match arm with the type error.
- Point at the prior match arms explicitly 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.

Fix rust-lang#46776, fix rust-lang#57206.
CC rust-lang#24157, rust-lang#38234.
  • Loading branch information
Centril authored Feb 14, 2019
2 parents b6ed206 + 802c897 commit 4ad3cf2
Show file tree
Hide file tree
Showing 14 changed files with 206 additions and 62 deletions.
33 changes: 21 additions & 12 deletions src/librustc/infer/error_reporting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -508,22 +508,31 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
}
}
}
ObligationCauseCode::MatchExpressionArm { arm_span, source } => match source {
ObligationCauseCode::MatchExpressionArm {
source,
ref prior_arms,
last_ty,
..
} => match source {
hir::MatchSource::IfLetDesugar { .. } => {
let msg = "`if let` arm with an incompatible type";
if self.tcx.sess.source_map().is_multiline(arm_span) {
err.span_note(arm_span, msg);
} else {
err.span_label(arm_span, msg);
}
let msg = "`if let` arms have incompatible types";
err.span_label(cause.span, msg);
}
hir::MatchSource::TryDesugar => {}
_ => {
let msg = "match arm with an incompatible type";
if self.tcx.sess.source_map().is_multiline(arm_span) {
err.span_note(arm_span, msg);
} else {
err.span_label(arm_span, msg);
let msg = "`match` arms have incompatible types";
err.span_label(cause.span, msg);
if prior_arms.len() <= 4 {
for sp in prior_arms {
err.span_label(*sp, format!(
"this is found to be of type `{}`",
last_ty,
));
}
} else if let Some(sp) = prior_arms.last() {
err.span_label(*sp, format!(
"this and all prior arms are found to be of type `{}`", last_ty,
));
}
}
},
Expand Down
3 changes: 3 additions & 0 deletions src/librustc/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ impl<'tcx> ObligationCause<'tcx> {
ObligationCauseCode::StartFunctionType => {
tcx.sess.source_map().def_span(self.span)
}
ObligationCauseCode::MatchExpressionArm { arm_span, .. } => arm_span,
_ => self.span,
}
}
Expand Down Expand Up @@ -223,6 +224,8 @@ pub enum ObligationCauseCode<'tcx> {
MatchExpressionArm {
arm_span: Span,
source: hir::MatchSource,
prior_arms: Vec<Span>,
last_ty: Ty<'tcx>,
},

/// Computing common supertype in the pattern guard for the arms of a match expression
Expand Down
17 changes: 14 additions & 3 deletions src/librustc/traits/structural_impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -513,10 +513,21 @@ impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> {
trait_item_def_id,
}),
super::ExprAssignable => Some(super::ExprAssignable),
super::MatchExpressionArm { arm_span, source } => Some(super::MatchExpressionArm {
super::MatchExpressionArm {
arm_span,
source: source,
}),
source,
ref prior_arms,
last_ty,
} => {
tcx.lift(&last_ty).map(|last_ty| {
super::MatchExpressionArm {
arm_span,
source,
prior_arms: prior_arms.clone(),
last_ty,
}
})
}
super::MatchExpressionArmPattern { span, ty } => {
tcx.lift(&ty).map(|ty| super::MatchExpressionArmPattern { span, ty })
}
Expand Down
29 changes: 25 additions & 4 deletions src/librustc_typeck/check/_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,8 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
CoerceMany::with_coercion_sites(coerce_first, arms)
};

let mut other_arms = vec![]; // used only for diagnostics
let mut prior_arm_ty = None;
for (i, (arm, pats_diverge)) in arms.iter().zip(all_arm_pats_diverge).enumerate() {
if let Some(ref g) = arm.guard {
self.diverges.set(pats_diverge);
Expand All @@ -709,17 +711,36 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
_ => false
};

let arm_span = if let hir::ExprKind::Block(ref blk, _) = arm.body.node {
// Point at the block expr instead of the entire block
blk.expr.as_ref().map(|e| e.span).unwrap_or(arm.body.span)
} else {
arm.body.span
};
if is_if_let_fallback {
let cause = self.cause(expr.span, ObligationCauseCode::IfExpressionWithNoElse);
assert!(arm_ty.is_unit());
coercion.coerce_forced_unit(self, &cause, &mut |_| (), true);
} else {
let cause = self.cause(expr.span, ObligationCauseCode::MatchExpressionArm {
arm_span: arm.body.span,
source: match_src
});
let cause = if i == 0 {
// The reason for the first arm to fail is not that the match arms diverge,
// but rather that there's a prior obligation that doesn't hold.
self.cause(arm_span, ObligationCauseCode::BlockTailExpression(arm.body.id))
} else {
self.cause(expr.span, ObligationCauseCode::MatchExpressionArm {
arm_span,
source: match_src,
prior_arms: other_arms.clone(),
last_ty: prior_arm_ty.unwrap(),
})
};
coercion.coerce(self, &cause, &arm.body, arm_ty);
}
other_arms.push(arm_span);
if other_arms.len() > 5 {
other_arms.remove(0);
}
prior_arm_ty = Some(arm_ty);
}

// We won't diverge unless the discriminant or all arms diverge.
Expand Down
9 changes: 5 additions & 4 deletions src/test/ui/if/if-let-arm-types.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
fn main() {
if let Some(b) = None { //~ ERROR: `if let` arms have incompatible types
//~^ expected (), found integer
//~| expected type `()`
//~| found type `{integer}`
if let Some(b) = None {
//~^ NOTE if let` arms have incompatible types
()
} else {
1
};
//~^^ ERROR: `if let` arms have incompatible types
//~| NOTE expected (), found integer
//~| NOTE expected type `()`
}
22 changes: 7 additions & 15 deletions src/test/ui/if/if-let-arm-types.stderr
Original file line number Diff line number Diff line change
@@ -1,25 +1,17 @@
error[E0308]: `if let` arms have incompatible types
--> $DIR/if-let-arm-types.rs:2:5
--> $DIR/if-let-arm-types.rs:6:9
|
LL | / if let Some(b) = None { //~ ERROR: `if let` arms have incompatible types
LL | | //~^ expected (), found integer
LL | | //~| expected type `()`
LL | | //~| found type `{integer}`
... |
LL | / if let Some(b) = None {
LL | | //~^ NOTE if let` arms have incompatible types
LL | | ()
LL | | } else {
LL | | 1
| | ^ expected (), found integer
LL | | };
| |_____^ expected (), found integer
| |_____- `if let` arms have incompatible types
|
= note: expected type `()`
found type `{integer}`
note: `if let` arm with an incompatible type
--> $DIR/if-let-arm-types.rs:7:12
|
LL | } else {
| ____________^
LL | | 1
LL | | };
| |_____^

error: aborting due to previous error

Expand Down
10 changes: 6 additions & 4 deletions src/test/ui/issues/issue-11319.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
fn main() {
match Some(10) {
//~^ ERROR match arms have incompatible types
//~| expected type `bool`
//~| found type `()`
//~| expected bool, found ()
//~^ NOTE `match` arms have incompatible types
Some(5) => false,
//~^ NOTE this is found to be of type `bool`
Some(2) => true,
//~^ NOTE this is found to be of type `bool`
None => (),
//~^ ERROR match arms have incompatible types
//~| NOTE expected bool, found ()
//~| NOTE expected type `bool`
_ => true
}
}
18 changes: 11 additions & 7 deletions src/test/ui/issues/issue-11319.stderr
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
error[E0308]: match arms have incompatible types
--> $DIR/issue-11319.rs:2:5
--> $DIR/issue-11319.rs:8:20
|
LL | / match Some(10) {
LL | | //~^ ERROR match arms have incompatible types
LL | | //~| expected type `bool`
LL | | //~| found type `()`
... |
LL | | //~^ NOTE `match` arms have incompatible types
LL | | Some(5) => false,
| | ----- this is found to be of type `bool`
LL | | //~^ NOTE this is found to be of type `bool`
LL | | Some(2) => true,
| | ---- this is found to be of type `bool`
LL | | //~^ NOTE this is found to be of type `bool`
LL | | None => (),
| | -- match arm with an incompatible type
| | ^^ expected bool, found ()
... |
LL | | _ => true
LL | | }
| |_____^ expected bool, found ()
| |_____- `match` arms have incompatible types
|
= note: expected type `bool`
found type `()`
Expand Down
3 changes: 2 additions & 1 deletion src/test/ui/issues/issue-17728.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ impl Debug for Player {
}

fn str_to_direction(to_parse: &str) -> RoomDirection {
match to_parse { //~ ERROR match arms have incompatible types
match to_parse {
"w" | "west" => RoomDirection::West,
"e" | "east" => RoomDirection::East,
"n" | "north" => RoomDirection::North,
Expand All @@ -108,6 +108,7 @@ fn str_to_direction(to_parse: &str) -> RoomDirection {
"down" => RoomDirection::Down,
_ => None
}
//~^^ ERROR match arms have incompatible types
}

fn main() {
Expand Down
10 changes: 6 additions & 4 deletions src/test/ui/issues/issue-17728.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,19 @@ LL | Some(entry) => Ok(entry),
| ^^^^^^^^^ ...but data from `room` is returned here

error[E0308]: match arms have incompatible types
--> $DIR/issue-17728.rs:100:5
--> $DIR/issue-17728.rs:109:14
|
LL | / match to_parse { //~ ERROR match arms have incompatible types
LL | / match to_parse {
LL | | "w" | "west" => RoomDirection::West,
LL | | "e" | "east" => RoomDirection::East,
LL | | "n" | "north" => RoomDirection::North,
... |
LL | | "down" => RoomDirection::Down,
| | ------------------- this and all prior arms are found to be of type `RoomDirection`
LL | | _ => None
| | ---- match arm with an incompatible type
| | ^^^^ expected enum `RoomDirection`, found enum `std::option::Option`
LL | | }
| |_____^ expected enum `RoomDirection`, found enum `std::option::Option`
| |_____- `match` arms have incompatible types
|
= note: expected type `RoomDirection`
found type `std::option::Option<_>`
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/issues/issue-24036.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ fn closure_to_loc() {

fn closure_from_match() {
let x = match 1usize {
//~^ ERROR match arms have incompatible types
1 => |c| c + 1,
2 => |c| c - 1,
_ => |c| c - 1
};
//~^^^ ERROR match arms have incompatible types
}

fn main() { }
14 changes: 7 additions & 7 deletions src/test/ui/issues/issue-24036.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,20 @@ LL | x = |c| c + 1;
= help: consider boxing your closure and/or using it as a trait object

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

Expand Down
45 changes: 45 additions & 0 deletions src/test/ui/match/match-type-err-first-arm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
fn main() {
let _ = test_func1(1);
let _ = test_func2(1);
}

fn test_func1(n: i32) -> i32 {
//~^ NOTE expected `i32` because of return type
match n {
12 => 'b',
//~^ ERROR mismatched types
//~| NOTE expected i32, found char
_ => 42,
}
}

fn test_func2(n: i32) -> i32 {
let x = match n {
//~^ NOTE `match` arms have incompatible types
12 => 'b',
//~^ NOTE this is found to be of type `char`
_ => 42,
//~^ ERROR match arms have incompatible types
//~| NOTE expected char, found integer
//~| NOTE expected type `char`
};
x
}

fn test_func3(n: i32) -> i32 {
let x = match n {
//~^ NOTE `match` arms have incompatible types
1 => 'b',
2 => 'b',
3 => 'b',
4 => 'b',
5 => 'b',
6 => 'b',
//~^ NOTE this and all prior arms are found to be of type `char`
_ => 42,
//~^ ERROR match arms have incompatible types
//~| NOTE expected char, found integer
//~| NOTE expected type `char`
};
x
}
Loading

0 comments on commit 4ad3cf2

Please sign in to comment.