Skip to content

Commit 976438f

Browse files
lilyballJakub Wieczorek
authored andcommitted
Produce a better error for irrefutable if let patterns
Modify ast::ExprMatch to include a new value of type ast::MatchSource, making it easy to tell whether the match was written literally or produced via desugaring. This allows us to customize error messages appropriately.
1 parent 1bc407f commit 976438f

File tree

20 files changed

+115
-23
lines changed

20 files changed

+115
-23
lines changed

src/librustc/diagnostics.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ register_diagnostic!(E0001, r##"
1919
one is too specific or the ordering is incorrect.
2020
"##)
2121

22+
register_diagnostic!(E0162, r##"
23+
This error is produced by an `if let` expression where the pattern is irrefutable.
24+
An `if let` that can never fail is considered an error.
25+
"##)
26+
2227
register_diagnostics!(
2328
E0002,
2429
E0003,

src/librustc/lint/builtin.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1091,7 +1091,10 @@ impl LintPass for UnnecessaryParens {
10911091
let (value, msg, struct_lit_needs_parens) = match e.node {
10921092
ast::ExprIf(ref cond, _, _) => (cond, "`if` condition", true),
10931093
ast::ExprWhile(ref cond, _, _) => (cond, "`while` condition", true),
1094-
ast::ExprMatch(ref head, _) => (head, "`match` head expression", true),
1094+
ast::ExprMatch(ref head, _, source) => match source {
1095+
ast::MatchNormal => (head, "`match` head expression", true),
1096+
ast::MatchIfLetDesugar => (head, "`if let` head expression", true)
1097+
},
10951098
ast::ExprRet(Some(ref value)) => (value, "`return` value", false),
10961099
ast::ExprAssign(_, ref value) => (value, "assigned value", false),
10971100
ast::ExprAssignOp(_, _, ref value) => (value, "assigned value", false),
@@ -1241,7 +1244,7 @@ impl LintPass for UnusedMut {
12411244

12421245
fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
12431246
match e.node {
1244-
ast::ExprMatch(_, ref arms) => {
1247+
ast::ExprMatch(_, ref arms, _) => {
12451248
for a in arms.iter() {
12461249
self.check_unused_mut_pat(cx, a.pats.as_slice())
12471250
}

src/librustc/middle/cfg/construct.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
324324
expr_exit
325325
}
326326

327-
ast::ExprMatch(ref discr, ref arms) => {
327+
ast::ExprMatch(ref discr, ref arms, _) => {
328328
//
329329
// [pred]
330330
// |

src/librustc/middle/check_match.rs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ pub fn check_crate(tcx: &ty::ctxt) {
147147
fn check_expr(cx: &mut MatchCheckCtxt, ex: &Expr) {
148148
visit::walk_expr(cx, ex);
149149
match ex.node {
150-
ExprMatch(ref scrut, ref arms) => {
150+
ExprMatch(ref scrut, ref arms, source) => {
151151
// First, check legality of move bindings.
152152
for arm in arms.iter() {
153153
check_legality_of_move_bindings(cx,
@@ -184,7 +184,7 @@ fn check_expr(cx: &mut MatchCheckCtxt, ex: &Expr) {
184184
}
185185

186186
// Fourth, check for unreachable arms.
187-
check_arms(cx, inlined_arms.as_slice());
187+
check_arms(cx, inlined_arms.as_slice(), source);
188188

189189
// Finally, check if the whole match expression is exhaustive.
190190
// Check for empty enum, because is_useful only works on inhabited types.
@@ -252,13 +252,31 @@ fn check_for_static_nan(cx: &MatchCheckCtxt, pats: &[P<Pat>]) {
252252
}
253253

254254
// Check for unreachable patterns
255-
fn check_arms(cx: &MatchCheckCtxt, arms: &[(Vec<P<Pat>>, Option<&Expr>)]) {
255+
fn check_arms(cx: &MatchCheckCtxt, arms: &[(Vec<P<Pat>>, Option<&Expr>)], source: MatchSource) {
256256
let mut seen = Matrix(vec![]);
257+
let mut printed_if_let_err = false;
257258
for &(ref pats, guard) in arms.iter() {
258259
for pat in pats.iter() {
259260
let v = vec![&**pat];
261+
260262
match is_useful(cx, &seen, v.as_slice(), LeaveOutWitness) {
261-
NotUseful => span_err!(cx.tcx.sess, pat.span, E0001, "unreachable pattern"),
263+
NotUseful => {
264+
if source == MatchIfLetDesugar {
265+
if printed_if_let_err {
266+
// we already printed an irrefutable if-let pattern error.
267+
// We don't want two, that's just confusing.
268+
} else {
269+
// find the first arm pattern so we can use its span
270+
let &(ref first_arm_pats, _) = &arms[0]; // we know there's at least 1 arm
271+
let first_pat = first_arm_pats.get(0); // and it's safe to assume 1 pat
272+
let span = first_pat.span;
273+
span_err!(cx.tcx.sess, span, E0162, "irrefutable if-let pattern");
274+
printed_if_let_err = true;
275+
}
276+
} else {
277+
span_err!(cx.tcx.sess, pat.span, E0001, "unreachable pattern");
278+
}
279+
}
262280
Useful => (),
263281
UsefulWithWitness(_) => unreachable!()
264282
}

src/librustc/middle/expr_use_visitor.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,TYPER> {
376376

377377
ast::ExprIfLet(..) => fail!("non-desugared ExprIfLet"),
378378

379-
ast::ExprMatch(ref discr, ref arms) => {
379+
ast::ExprMatch(ref discr, ref arms, _) => {
380380
let discr_cmt = return_if_err!(self.mc.cat_expr(&**discr));
381381
self.borrow_expr(&**discr, ty::ReEmpty, ty::ImmBorrow, MatchDiscriminant);
382382

src/librustc/middle/liveness.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1029,7 +1029,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
10291029
self.propagate_through_loop(expr, LoopLoop, &**blk, succ)
10301030
}
10311031

1032-
ExprMatch(ref e, ref arms) => {
1032+
ExprMatch(ref e, ref arms, _) => {
10331033
//
10341034
// (e)
10351035
// |

src/librustc/middle/trans/debuginfo.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3659,7 +3659,7 @@ fn populate_scope_map(cx: &CrateContext,
36593659
}
36603660
}
36613661

3662-
ast::ExprMatch(ref discriminant_exp, ref arms) => {
3662+
ast::ExprMatch(ref discriminant_exp, ref arms, _) => {
36633663
walk_expr(cx, &**discriminant_exp, scope_stack, scope_map);
36643664

36653665
// For each arm we have to first walk the pattern as these might

src/librustc/middle/trans/expr.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1013,7 +1013,7 @@ fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
10131013
ast::ExprIf(ref cond, ref thn, ref els) => {
10141014
controlflow::trans_if(bcx, expr.id, &**cond, &**thn, els.as_ref().map(|e| &**e), dest)
10151015
}
1016-
ast::ExprMatch(ref discr, ref arms) => {
1016+
ast::ExprMatch(ref discr, ref arms, _) => {
10171017
_match::trans_match(bcx, expr, &**discr, arms.as_slice(), dest)
10181018
}
10191019
ast::ExprBlock(ref blk) => {

src/librustc/middle/typeck/check/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4144,7 +4144,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
41444144
fcx.write_nil(id);
41454145
}
41464146
}
4147-
ast::ExprMatch(ref discrim, ref arms) => {
4147+
ast::ExprMatch(ref discrim, ref arms, _) => {
41484148
_match::check_match(fcx, expr, &**discrim, arms.as_slice());
41494149
}
41504150
ast::ExprFnBlock(_, ref decl, ref body) => {

src/librustc/middle/typeck/check/regionck.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -725,7 +725,7 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) {
725725
visit::walk_expr(rcx, expr);
726726
}
727727

728-
ast::ExprMatch(ref discr, ref arms) => {
728+
ast::ExprMatch(ref discr, ref arms, _) => {
729729
link_match(rcx, &**discr, arms.as_slice());
730730

731731
visit::walk_expr(rcx, expr);

0 commit comments

Comments
 (0)