Skip to content

Commit e497a9d

Browse files
committed
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 89b29f4 commit e497a9d

File tree

20 files changed

+51
-24
lines changed

20 files changed

+51
-24
lines changed

src/librustc/diagnostics.rs

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

20+
register_diagnostic!(E0159, r##"
21+
This error is produced by an `if let` expression where the pattern is irrefutable.
22+
An `if let` that can never fail is considered an error.
23+
"##)
24+
2025
register_diagnostics!(
2126
E0002,
2227
E0003,

src/librustc/front/config.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,15 +206,15 @@ fn fold_block(cx: &mut Context, b: ast::P<ast::Block>) -> ast::P<ast::Block> {
206206

207207
fn fold_expr(cx: &mut Context, expr: Gc<ast::Expr>) -> Gc<ast::Expr> {
208208
let expr = match expr.node {
209-
ast::ExprMatch(ref m, ref arms) => {
209+
ast::ExprMatch(ref m, ref arms, source) => {
210210
let arms = arms.iter()
211211
.filter(|a| (cx.in_cfg)(a.attrs.as_slice()))
212212
.map(|a| a.clone())
213213
.collect();
214214
box(GC) ast::Expr {
215215
id: expr.id,
216216
span: expr.span.clone(),
217-
node: ast::ExprMatch(m.clone(), arms),
217+
node: ast::ExprMatch(m.clone(), arms, source),
218218
}
219219
}
220220
_ => expr.clone()

src/librustc/lint/builtin.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1080,7 +1080,10 @@ impl LintPass for UnnecessaryParens {
10801080
let (value, msg, struct_lit_needs_parens) = match e.node {
10811081
ast::ExprIf(cond, _, _) => (cond, "`if` condition", true),
10821082
ast::ExprWhile(cond, _) => (cond, "`while` condition", true),
1083-
ast::ExprMatch(head, _) => (head, "`match` head expression", true),
1083+
ast::ExprMatch(head, _, source) => match source {
1084+
ast::MatchNormal => (head, "`match` head expression", true),
1085+
ast::MatchIfLetDesugar => (head, "`if let` head expression", true)
1086+
},
10841087
ast::ExprRet(Some(value)) => (value, "`return` value", false),
10851088
ast::ExprAssign(_, value) => (value, "assigned value", false),
10861089
ast::ExprAssignOp(_, _, value) => (value, "assigned value", false),
@@ -1192,7 +1195,7 @@ impl LintPass for UnusedMut {
11921195

11931196
fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
11941197
match e.node {
1195-
ast::ExprMatch(_, ref arms) => {
1198+
ast::ExprMatch(_, ref arms, _) => {
11961199
for a in arms.iter() {
11971200
self.check_unused_mut_pat(cx, a.pats.as_slice())
11981201
}

src/librustc/middle/cfg/construct.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ impl<'a> CFGBuilder<'a> {
327327
expr_exit
328328
}
329329

330-
ast::ExprMatch(ref discr, ref arms) => {
330+
ast::ExprMatch(ref discr, ref arms, _) => {
331331
//
332332
// [pred]
333333
// |

src/librustc/middle/check_match.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ pub fn check_crate(tcx: &ty::ctxt, krate: &Crate) {
139139
fn check_expr(cx: &mut MatchCheckCtxt, ex: &Expr) {
140140
visit::walk_expr(cx, ex, ());
141141
match ex.node {
142-
ExprMatch(scrut, ref arms) => {
142+
ExprMatch(scrut, ref arms, source) => {
143143
// First, check legality of move bindings.
144144
for arm in arms.iter() {
145145
check_legality_of_move_bindings(cx,
@@ -178,7 +178,7 @@ fn check_expr(cx: &mut MatchCheckCtxt, ex: &Expr) {
178178
check_for_static_nan(cx, inlined_arms.as_slice());
179179

180180
// Fourth, check for unreachable arms.
181-
check_arms(cx, inlined_arms.as_slice());
181+
check_arms(cx, inlined_arms.as_slice(), source);
182182

183183
// Finally, check if the whole match expression is exhaustive.
184184
// Check for empty enum, because is_useful only works on inhabited types.
@@ -251,13 +251,23 @@ fn check_for_static_nan(cx: &MatchCheckCtxt, arms: &[Arm]) {
251251
}
252252

253253
// Check for unreachable patterns
254-
fn check_arms(cx: &MatchCheckCtxt, arms: &[Arm]) {
254+
fn check_arms(cx: &MatchCheckCtxt, arms: &[Arm], source: MatchSource) {
255255
let mut seen = Matrix(vec!());
256256
for arm in arms.iter() {
257257
for &pat in arm.pats.iter() {
258258
let v = vec![pat];
259259
match is_useful(cx, &seen, v.as_slice(), LeaveOutWitness) {
260-
NotUseful => span_err!(cx.tcx.sess, pat.span, E0001, "unreachable pattern"),
260+
NotUseful => {
261+
if source == MatchIfLetDesugar {
262+
// find the first arm pattern so we can use its span
263+
let first_arm = &arms[0]; // we know there's at least 1 arm
264+
let first_pat = first_arm.pats.get(0); // and it's safe to assume 1 pat
265+
let span = first_pat.span;
266+
span_err!(cx.tcx.sess, span, E0159, "irrefutable if-let pattern")
267+
} else {
268+
span_err!(cx.tcx.sess, pat.span, E0001, "unreachable pattern")
269+
}
270+
}
261271
Useful => (),
262272
UsefulWithWitness(_) => unreachable!()
263273
}

src/librustc/middle/expr_use_visitor.rs

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

359359
ast::ExprIfLet(..) => fail!("non-desugared ExprIfLet"),
360360

361-
ast::ExprMatch(ref discr, ref arms) => {
361+
ast::ExprMatch(ref discr, ref arms, _) => {
362362
// treatment of the discriminant is handled while
363363
// walking the arms:
364364
self.walk_expr(&**discr);

src/librustc/middle/liveness.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1038,7 +1038,7 @@ impl<'a> Liveness<'a> {
10381038
self.propagate_through_loop(expr, LoopLoop, &**blk, succ)
10391039
}
10401040

1041-
ExprMatch(ref e, ref arms) => {
1041+
ExprMatch(ref e, ref arms, _) => {
10421042
//
10431043
// (e)
10441044
// |

src/librustc/middle/trans/debuginfo.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3686,7 +3686,7 @@ fn populate_scope_map(cx: &CrateContext,
36863686
}
36873687
}
36883688

3689-
ast::ExprMatch(ref discriminant_exp, ref arms) => {
3689+
ast::ExprMatch(ref discriminant_exp, ref arms, _) => {
36903690
walk_expr(cx, &**discriminant_exp, scope_stack, scope_map);
36913691

36923692
// 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
@@ -739,7 +739,7 @@ fn trans_rvalue_dps_unadjusted<'a>(bcx: &'a Block<'a>,
739739
ast::ExprIf(ref cond, ref thn, els) => {
740740
controlflow::trans_if(bcx, expr.id, &**cond, thn.clone(), els, dest)
741741
}
742-
ast::ExprMatch(ref discr, ref arms) => {
742+
ast::ExprMatch(ref discr, ref arms, _) => {
743743
_match::trans_match(bcx, expr, &**discr, arms.as_slice(), dest)
744744
}
745745
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
@@ -3469,7 +3469,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
34693469
fcx.write_nil(id);
34703470
}
34713471
}
3472-
ast::ExprMatch(ref discrim, ref arms) => {
3472+
ast::ExprMatch(ref discrim, ref arms, _) => {
34733473
_match::check_match(fcx, expr, &**discrim, arms.as_slice());
34743474
}
34753475
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
@@ -591,7 +591,7 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) {
591591
visit::walk_expr(rcx, expr, ());
592592
}
593593

594-
ast::ExprMatch(ref discr, ref arms) => {
594+
ast::ExprMatch(ref discr, ref arms, _) => {
595595
link_match(rcx, &**discr, arms.as_slice());
596596

597597
visit::walk_expr(rcx, expr, ());

src/librustc/util/ppaux.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ pub fn explain_region_and_span(cx: &ctxt, region: ty::Region)
8080
ast::ExprMethodCall(..) => {
8181
explain_span(cx, "method call", expr.span)
8282
},
83+
ast::ExprMatch(_, _, ast::MatchIfLetDesugar) => explain_span(cx, "if let", expr.span),
8384
ast::ExprMatch(..) => explain_span(cx, "match", expr.span),
8485
_ => explain_span(cx, "expression", expr.span)
8586
}

src/libsyntax/ast.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -541,7 +541,7 @@ pub enum Expr_ {
541541
// Conditionless loop (can be exited with break, cont, or ret)
542542
// FIXME #6993: change to Option<Name> ... or not, if these are hygienic.
543543
ExprLoop(P<Block>, Option<Ident>),
544-
ExprMatch(Gc<Expr>, Vec<Arm>),
544+
ExprMatch(Gc<Expr>, Vec<Arm>, MatchSource),
545545
ExprFnBlock(CaptureClause, P<FnDecl>, P<Block>),
546546
ExprProc(P<FnDecl>, P<Block>),
547547
ExprUnboxedFn(CaptureClause, UnboxedClosureKind, P<FnDecl>, P<Block>),
@@ -575,6 +575,12 @@ pub enum Expr_ {
575575
ExprParen(Gc<Expr>)
576576
}
577577

578+
#[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)]
579+
pub enum MatchSource {
580+
MatchNormal,
581+
MatchIfLetDesugar
582+
}
583+
578584
#[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)]
579585
pub enum CaptureClause {
580586
CaptureByValue,

src/libsyntax/ext/build.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -860,7 +860,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
860860

861861
fn expr_match(&self, span: Span, arg: Gc<ast::Expr>,
862862
arms: Vec<ast::Arm>) -> Gc<Expr> {
863-
self.expr(span, ast::ExprMatch(arg, arms))
863+
self.expr(span, ast::ExprMatch(arg, arms, ast::MatchNormal))
864864
}
865865

866866
fn expr_if(&self, span: Span,

src/libsyntax/ext/expand.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ fn expand_expr(e: Gc<ast::Expr>, fld: &mut MacroExpander) -> Gc<ast::Expr> {
122122
arms.push_all_move(else_if_arms);
123123
arms.push(else_arm);
124124

125-
let match_expr = fld.cx.expr_match(span, expr, arms);
125+
let match_expr = fld.cx.expr(span, ast::ExprMatch(expr, arms, ast::MatchIfLetDesugar));
126126
fld.fold_expr(match_expr)
127127
}
128128

src/libsyntax/fold.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1148,9 +1148,10 @@ pub fn noop_fold_expr<T: Folder>(e: Gc<Expr>, folder: &mut T) -> Gc<Expr> {
11481148
ExprLoop(folder.fold_block(body),
11491149
opt_ident.map(|x| folder.fold_ident(x)))
11501150
}
1151-
ExprMatch(expr, ref arms) => {
1151+
ExprMatch(expr, ref arms, source) => {
11521152
ExprMatch(folder.fold_expr(expr),
1153-
arms.iter().map(|x| folder.fold_arm(x)).collect())
1153+
arms.iter().map(|x| folder.fold_arm(x)).collect(),
1154+
source)
11541155
}
11551156
ExprFnBlock(capture_clause, ref decl, ref body) => {
11561157
ExprFnBlock(capture_clause,

src/libsyntax/parse/parser.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ use ast::{ItemEnum, ItemFn, ItemForeignMod, ItemImpl};
3838
use ast::{ItemMac, ItemMod, ItemStruct, ItemTrait, ItemTy, Lit, Lit_};
3939
use ast::{LitBool, LitChar, LitByte, LitBinary};
4040
use ast::{LitNil, LitStr, LitInt, Local, LocalLet};
41-
use ast::{MutImmutable, MutMutable, Mac_, MacInvocTT, Matcher, MatchNonterminal};
41+
use ast::{MutImmutable, MutMutable, Mac_, MacInvocTT, Matcher, MatchNonterminal, MatchNormal};
4242
use ast::{MatchSeq, MatchTok, Method, MutTy, BiMul, Mutability};
4343
use ast::{MethodImplItem};
4444
use ast::{NamedField, UnNeg, NoReturn, UnNot, P, Pat, PatEnum};
@@ -2825,7 +2825,7 @@ impl<'a> Parser<'a> {
28252825
}
28262826
let hi = self.span.hi;
28272827
self.bump();
2828-
return self.mk_expr(lo, hi, ExprMatch(discriminant, arms));
2828+
return self.mk_expr(lo, hi, ExprMatch(discriminant, arms, MatchNormal));
28292829
}
28302830

28312831
pub fn parse_arm(&mut self) -> Arm {

src/libsyntax/print/pprust.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1519,7 +1519,7 @@ impl<'a> State<'a> {
15191519
try!(space(&mut self.s));
15201520
try!(self.print_block(&**blk));
15211521
}
1522-
ast::ExprMatch(ref expr, ref arms) => {
1522+
ast::ExprMatch(ref expr, ref arms, _) => {
15231523
try!(self.cbox(indent_unit));
15241524
try!(self.ibox(4));
15251525
try!(self.word_nbsp("match"));

src/libsyntax/visit.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -800,7 +800,7 @@ pub fn walk_expr<E: Clone, V: Visitor<E>>(visitor: &mut V, expression: &Expr, en
800800
visitor.visit_block(&**block, env.clone())
801801
}
802802
ExprLoop(ref block, _) => visitor.visit_block(&**block, env.clone()),
803-
ExprMatch(ref subexpression, ref arms) => {
803+
ExprMatch(ref subexpression, ref arms, _) => {
804804
visitor.visit_expr(&**subexpression, env.clone());
805805
for arm in arms.iter() {
806806
visitor.visit_arm(arm, env.clone())

src/test/compile-fail/lint-unnecessary-parens.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ fn main() {
3232
match (true) { //~ ERROR unnecessary parentheses around `match` head expression
3333
_ => {}
3434
}
35+
if let 1i = (1i) {} //~ ERROR unnecessary parentheses around `if let` head expression
3536
let v = X { y: false };
3637
// struct lits needs parens, so these shouldn't warn.
3738
if (v == X { y: true }) {}

0 commit comments

Comments
 (0)