diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index aff925d108272..93a46090b90eb 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -133,6 +133,12 @@ declare_lint! { "type parameter default erroneously allowed in invalid location" } +declare_lint! { + pub MATCH_OF_UNIT_VARIANT_VIA_PAREN_DOTDOT, + Warn, + "unit struct or enum variant erroneously allowed to match via path::ident(..)" +} + /// Does nothing as a lint pass, but registers some `Lint`s /// which are used by other parts of the compiler. #[derive(Copy, Clone)] @@ -159,6 +165,7 @@ impl LintPass for HardwiredLints { TRIVIAL_NUMERIC_CASTS, PRIVATE_IN_PUBLIC, INVALID_TYPE_PARAM_DEFAULT, + MATCH_OF_UNIT_VARIANT_VIA_PAREN_DOTDOT, CONST_ERR ) } diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index f2e75960406f1..b4f398053d1fd 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -144,7 +144,8 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { UNUSED_UNSAFE, PATH_STATEMENTS, UNUSED_ATTRIBUTES); add_lint_group!(sess, FUTURE_INCOMPATIBLE, - PRIVATE_IN_PUBLIC, INVALID_TYPE_PARAM_DEFAULT); + PRIVATE_IN_PUBLIC, INVALID_TYPE_PARAM_DEFAULT, + MATCH_OF_UNIT_VARIANT_VIA_PAREN_DOTDOT); // We have one lint pass defined specially store.register_late_pass(sess, false, box lint::GatherNodeLevels); diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index 926d7fd6e25eb..93ceaf8b11f12 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -19,6 +19,7 @@ use check::{check_expr, check_expr_has_type, check_expr_with_expectation}; use check::{check_expr_coercable_to_type, demand, FnCtxt, Expectation}; use check::{check_expr_with_lvalue_pref}; use check::{instantiate_path, resolve_ty_and_def_ufcs, structurally_resolved_type}; +use lint; use require_same_types; use util::nodemap::FnvHashMap; use session::Session; @@ -138,7 +139,7 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, if pat_is_resolved_const(&tcx.def_map.borrow(), pat) => { if let hir::PatEnum(ref path, ref subpats) = pat.node { if !(subpats.is_some() && subpats.as_ref().unwrap().is_empty()) { - bad_struct_kind_err(tcx.sess, pat.span, path, false); + bad_struct_kind_err(tcx.sess, pat, path, false); return; } } @@ -590,10 +591,21 @@ pub fn check_pat_struct<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, pat: &'tcx hir::Pat, } // This function exists due to the warning "diagnostic code E0164 already used" -fn bad_struct_kind_err(sess: &Session, span: Span, path: &hir::Path, is_warning: bool) { +fn bad_struct_kind_err(sess: &Session, pat: &hir::Pat, path: &hir::Path, lint: bool) { let name = pprust::path_to_string(path); - span_err_or_warn!(is_warning, sess, span, E0164, - "`{}` does not name a tuple variant or a tuple struct", name); + let msg = format!("`{}` does not name a tuple variant or a tuple struct", name); + if lint { + let expanded_msg = + format!("{}; RFC 218 disallowed matching of unit variants or unit structs via {}(..)", + msg, + name); + sess.add_lint(lint::builtin::MATCH_OF_UNIT_VARIANT_VIA_PAREN_DOTDOT, + pat.id, + pat.span, + expanded_msg); + } else { + span_err!(sess, pat.span, E0164, "{}", msg); + } } pub fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, @@ -657,11 +669,8 @@ pub fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, opt_ty, def, pat.span, pat.id); let report_bad_struct_kind = |is_warning| { - bad_struct_kind_err(tcx.sess, pat.span, path, is_warning); - if is_warning { - return; - } - + bad_struct_kind_err(tcx.sess, pat, path, is_warning); + if is_warning { return; } fcx.write_error(pat.id); if let Some(subpats) = subpats { for pat in subpats { @@ -699,12 +708,6 @@ pub fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, report_bad_struct_kind(is_special_case); if !is_special_case { return - } else { - // Boo! Too painful to attach this to the actual warning, - // it should go away at some point though. - tcx.sess.span_note_without_error(pat.span, - "this warning will become a HARD ERROR in a future release. \ - See RFC 218 for details."); } } (variant.fields @@ -718,7 +721,10 @@ pub fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, ty::TyStruct(struct_def, expected_substs) => { let variant = struct_def.struct_variant(); if is_tuple_struct_pat && variant.kind() != ty::VariantKind::Tuple { - report_bad_struct_kind(false); + // Matching unit structs with tuple variant patterns (`UnitVariant(..)`) + // is allowed for backward compatibility. + let is_special_case = variant.kind() == ty::VariantKind::Unit; + report_bad_struct_kind(is_special_case); return; } (variant.fields diff --git a/src/test/compile-fail/empty-struct-unit-pat.rs b/src/test/compile-fail/empty-struct-unit-pat.rs index 6cb9a3f007f0c..7e13f539bb043 100644 --- a/src/test/compile-fail/empty-struct-unit-pat.rs +++ b/src/test/compile-fail/empty-struct-unit-pat.rs @@ -10,6 +10,8 @@ // Can't use unit struct as enum pattern +#![feature(rustc_attrs)] +// remove prior feature after warning cycle and promoting warnings to errors #![feature(braced_empty_structs)] struct Empty1; @@ -18,7 +20,9 @@ enum E { Empty2 } -fn main() { +// remove attribute after warning cycle and promoting warnings to errors +#[rustc_error] +fn main() { //~ ERROR: compilation successful let e1 = Empty1; let e2 = E::Empty2; @@ -27,7 +31,7 @@ fn main() { // Empty1() => () // ERROR `Empty1` does not name a tuple variant or a tuple struct // } match e1 { - Empty1(..) => () //~ ERROR `Empty1` does not name a tuple variant or a tuple struct + Empty1(..) => () //~ WARN `Empty1` does not name a tuple variant or a tuple struct } // Rejected by parser as yet // match e2 { diff --git a/src/test/compile-fail/match-pattern-field-mismatch-2.rs b/src/test/compile-fail/match-pattern-field-mismatch-2.rs index 17debdabb61f0..e63ddf6c7fd9b 100644 --- a/src/test/compile-fail/match-pattern-field-mismatch-2.rs +++ b/src/test/compile-fail/match-pattern-field-mismatch-2.rs @@ -21,7 +21,6 @@ fn main() { color::cmyk(_, _, _, _) => { } color::no_color(_) => { } //~^ ERROR this pattern has 1 field, but the corresponding variant has no fields - //~^^ WARN `color::no_color` does not name a tuple variant or a tuple struct } } } diff --git a/src/test/compile-fail/pattern-error-continue.rs b/src/test/compile-fail/pattern-error-continue.rs index 1721d1f0ae11c..aa7202574abfc 100644 --- a/src/test/compile-fail/pattern-error-continue.rs +++ b/src/test/compile-fail/pattern-error-continue.rs @@ -26,7 +26,6 @@ fn main() { match A::B(1, 2) { A::B(_, _, _) => (), //~ ERROR this pattern has 3 fields, but A::D(_) => (), //~ ERROR this pattern has 1 field, but - //~^ WARN `A::D` does not name a tuple variant or a tuple struct _ => () } match 'c' {