From 7023bea22c969a324d7d95d8794370410ff7c4c9 Mon Sep 17 00:00:00 2001 From: Barosl Lee Date: Sat, 20 Dec 2014 01:42:21 +0900 Subject: [PATCH 1/2] Print a friendly error for the if-let construct without an else block Fixes #19991. --- src/librustc_typeck/check/_match.rs | 25 ++++++++++++++++++++----- src/librustc_typeck/check/mod.rs | 4 ++-- src/libsyntax/ast.rs | 2 +- src/libsyntax/ext/expand.rs | 4 +++- src/test/compile-fail/issue-19991.rs | 18 ++++++++++++++++++ 5 files changed, 44 insertions(+), 9 deletions(-) create mode 100644 src/test/compile-fail/issue-19991.rs diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index 44cc5fce53d..d4b89621ace 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -238,7 +238,8 @@ pub fn check_match<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, expr: &ast::Expr, discrim: &ast::Expr, arms: &[ast::Arm], - expected: Expectation<'tcx>) { + expected: Expectation<'tcx>, + match_src: ast::MatchSource) { let tcx = fcx.ccx.tcx; let discrim_ty = fcx.infcx().next_ty_var(); @@ -290,12 +291,26 @@ pub fn check_match<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, if ty::type_is_error(result_ty) || ty::type_is_error(bty) { ty::mk_err() } else { + let (origin, expected, found) = match match_src { + /* if-let construct without an else block */ + ast::MatchIfLetDesugar(contains_else_arm) if !contains_else_arm => ( + infer::IfExpressionWithNoElse(expr.span), + bty, + result_ty, + ), + _ => ( + infer::MatchExpressionArm(expr.span, arm.body.span), + result_ty, + bty, + ), + }; + infer::common_supertype( fcx.infcx(), - infer::MatchExpressionArm(expr.span, arm.body.span), - true, // result_ty is "expected" here - result_ty, - bty + origin, + true, + expected, + found, ) } }); diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index bbc33826f35..f8a8ef35f75 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -3918,8 +3918,8 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, fcx.write_nil(id); } } - ast::ExprMatch(ref discrim, ref arms, _) => { - _match::check_match(fcx, expr, &**discrim, arms.as_slice(), expected); + ast::ExprMatch(ref discrim, ref arms, match_src) => { + _match::check_match(fcx, expr, &**discrim, arms.as_slice(), expected, match_src); } ast::ExprClosure(_, opt_kind, ref decl, ref body) => { closure::check_expr_closure(fcx, expr, opt_kind, &**decl, &**body, expected); diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index be8f32bc4d5..ab338da63bf 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -754,7 +754,7 @@ pub struct QPath { #[deriving(Clone, Copy, PartialEq, Eq, Encodable, Decodable, Hash, Show)] pub enum MatchSource { MatchNormal, - MatchIfLetDesugar, + MatchIfLetDesugar(bool /* contains_else_arm */), MatchWhileLetDesugar, } diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 20c8ff20b71..63bd38de8a0 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -170,7 +170,9 @@ pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { arms.extend(else_if_arms.into_iter()); arms.push(else_arm); - let match_expr = fld.cx.expr(span, ast::ExprMatch(expr, arms, ast::MatchIfLetDesugar)); + let match_expr = fld.cx.expr(span, ast::ExprMatch(expr, + arms, + ast::MatchIfLetDesugar(elseopt.is_some()))); fld.fold_expr(match_expr) } diff --git a/src/test/compile-fail/issue-19991.rs b/src/test/compile-fail/issue-19991.rs new file mode 100644 index 00000000000..0f1dbfa3492 --- /dev/null +++ b/src/test/compile-fail/issue-19991.rs @@ -0,0 +1,18 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test if the sugared if-let construct correctly prints "missing an else clause" when an else +// clause does not exist, instead of the unsympathetic "match arms have incompatible types" + +fn main() { + if let Some(homura) = Some("madoka") { //~ ERROR missing an else clause: expected `()` + 765i32 + }; +} From 314ed2df096858e7c174254b0babd5f949ae6d27 Mon Sep 17 00:00:00 2001 From: Barosl Lee Date: Sat, 20 Dec 2014 07:58:02 +0900 Subject: [PATCH 2/2] Drop the Match prefix from the MatchSource variants --- src/librustc/lint/builtin.rs | 6 +++--- src/librustc/middle/check_match.rs | 6 +++--- src/librustc/util/ppaux.rs | 5 +++-- src/librustc_typeck/check/_match.rs | 3 ++- src/libsyntax/ast.rs | 7 +++---- src/libsyntax/ext/build.rs | 2 +- src/libsyntax/ext/expand.rs | 12 ++++++++---- src/libsyntax/parse/parser.rs | 4 ++-- 8 files changed, 25 insertions(+), 20 deletions(-) diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 88b12aa5660..f5c7ac16478 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -1157,9 +1157,9 @@ impl LintPass for UnusedParens { ast::ExprIf(ref cond, _, _) => (cond, "`if` condition", true), ast::ExprWhile(ref cond, _, _) => (cond, "`while` condition", true), ast::ExprMatch(ref head, _, source) => match source { - ast::MatchNormal => (head, "`match` head expression", true), - ast::MatchIfLetDesugar => (head, "`if let` head expression", true), - ast::MatchWhileLetDesugar => (head, "`while let` head expression", true), + ast::MatchSource::Normal => (head, "`match` head expression", true), + ast::MatchSource::IfLetDesugar { .. } => (head, "`if let` head expression", true), + ast::MatchSource::WhileLetDesugar => (head, "`while let` head expression", true), }, ast::ExprRet(Some(ref value)) => (value, "`return` value", false), ast::ExprAssign(_, ref value) => (value, "assigned value", false), diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs index 79e776c3308..ca338f5d02a 100644 --- a/src/librustc/middle/check_match.rs +++ b/src/librustc/middle/check_match.rs @@ -307,7 +307,7 @@ fn check_arms(cx: &MatchCheckCtxt, match is_useful(cx, &seen, v.as_slice(), LeaveOutWitness) { NotUseful => { match source { - ast::MatchIfLetDesugar => { + ast::MatchSource::IfLetDesugar { .. } => { if printed_if_let_err { // we already printed an irrefutable if-let pattern error. // We don't want two, that's just confusing. @@ -321,7 +321,7 @@ fn check_arms(cx: &MatchCheckCtxt, } }, - ast::MatchWhileLetDesugar => { + ast::MatchSource::WhileLetDesugar => { // find the first arm pattern so we can use its span let &(ref first_arm_pats, _) = &arms[0]; let first_pat = &first_arm_pats[0]; @@ -329,7 +329,7 @@ fn check_arms(cx: &MatchCheckCtxt, span_err!(cx.tcx.sess, span, E0165, "irrefutable while-let pattern"); }, - ast::MatchNormal => { + ast::MatchSource::Normal => { span_err!(cx.tcx.sess, pat.span, E0001, "unreachable pattern") }, } diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index b0124977c9f..71146918d99 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -93,8 +93,9 @@ pub fn explain_region_and_span(cx: &ctxt, region: ty::Region) ast::ExprMethodCall(..) => { explain_span(cx, "method call", expr.span) }, - ast::ExprMatch(_, _, ast::MatchIfLetDesugar) => explain_span(cx, "if let", expr.span), - ast::ExprMatch(_, _, ast::MatchWhileLetDesugar) => { + ast::ExprMatch(_, _, ast::MatchSource::IfLetDesugar { .. }) => + explain_span(cx, "if let", expr.span), + ast::ExprMatch(_, _, ast::MatchSource::WhileLetDesugar) => { explain_span(cx, "while let", expr.span) }, ast::ExprMatch(..) => explain_span(cx, "match", expr.span), diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index d4b89621ace..3b48808b362 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -293,7 +293,8 @@ pub fn check_match<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, } else { let (origin, expected, found) = match match_src { /* if-let construct without an else block */ - ast::MatchIfLetDesugar(contains_else_arm) if !contains_else_arm => ( + ast::MatchSource::IfLetDesugar { contains_else_clause } + if !contains_else_clause => ( infer::IfExpressionWithNoElse(expr.span), bty, result_ty, diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index ab338da63bf..cb0254a7ec5 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -32,7 +32,6 @@ pub use self::LitIntType::*; pub use self::LocalSource::*; pub use self::Mac_::*; pub use self::MacStmtStyle::*; -pub use self::MatchSource::*; pub use self::MetaItem_::*; pub use self::Method_::*; pub use self::Mutability::*; @@ -753,9 +752,9 @@ pub struct QPath { #[deriving(Clone, Copy, PartialEq, Eq, Encodable, Decodable, Hash, Show)] pub enum MatchSource { - MatchNormal, - MatchIfLetDesugar(bool /* contains_else_arm */), - MatchWhileLetDesugar, + Normal, + IfLetDesugar { contains_else_clause: bool }, + WhileLetDesugar, } #[deriving(Clone, Copy, PartialEq, Eq, Encodable, Decodable, Hash, Show)] diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index d35091f8ab0..9d4992f7453 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -868,7 +868,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> { } fn expr_match(&self, span: Span, arg: P, arms: Vec) -> P { - self.expr(span, ast::ExprMatch(arg, arms, ast::MatchNormal)) + self.expr(span, ast::ExprMatch(arg, arms, ast::MatchSource::Normal)) } fn expr_if(&self, span: Span, cond: P, diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 63bd38de8a0..bf19eecbf65 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -97,7 +97,7 @@ pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { // `match { ... }` let arms = vec![pat_arm, break_arm]; let match_expr = fld.cx.expr(span, - ast::ExprMatch(expr, arms, ast::MatchWhileLetDesugar)); + ast::ExprMatch(expr, arms, ast::MatchSource::WhileLetDesugar)); // `[opt_ident]: loop { ... }` let loop_block = fld.cx.block_expr(match_expr); @@ -158,6 +158,8 @@ pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { arms }; + let contains_else_clause = elseopt.is_some(); + // `_ => [ | ()]` let else_arm = { let pat_under = fld.cx.pat_wild(span); @@ -170,9 +172,11 @@ pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { arms.extend(else_if_arms.into_iter()); arms.push(else_arm); - let match_expr = fld.cx.expr(span, ast::ExprMatch(expr, - arms, - ast::MatchIfLetDesugar(elseopt.is_some()))); + let match_expr = fld.cx.expr(span, + ast::ExprMatch(expr, arms, + ast::MatchSource::IfLetDesugar { + contains_else_clause: contains_else_clause, + })); fld.fold_expr(match_expr) } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 3ad224b93ce..b6efbecc78a 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -41,7 +41,7 @@ use ast::{LifetimeDef, Lit, Lit_}; use ast::{LitBool, LitChar, LitByte, LitBinary}; use ast::{LitStr, LitInt, Local, LocalLet}; use ast::{MacStmtWithBraces, MacStmtWithSemicolon, MacStmtWithoutBraces}; -use ast::{MutImmutable, MutMutable, Mac_, MacInvocTT, MatchNormal}; +use ast::{MutImmutable, MutMutable, Mac_, MacInvocTT, MatchSource}; use ast::{Method, MutTy, BiMul, Mutability}; use ast::{MethodImplItem, NamedField, UnNeg, NoReturn, NodeId, UnNot}; use ast::{Pat, PatEnum, PatIdent, PatLit, PatRange, PatRegion, PatStruct}; @@ -3114,7 +3114,7 @@ impl<'a> Parser<'a> { } let hi = self.span.hi; self.bump(); - return self.mk_expr(lo, hi, ExprMatch(discriminant, arms, MatchNormal)); + return self.mk_expr(lo, hi, ExprMatch(discriminant, arms, MatchSource::Normal)); } pub fn parse_arm(&mut self) -> Arm {