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' {