diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs
index e0532a3320bc4..1fe57bc338fd5 100644
--- a/src/librustc/session/config.rs
+++ b/src/librustc/session/config.rs
@@ -28,7 +28,7 @@ use rustc_data_structures::stable_hasher::ToStableHashKey;
 use lint;
 use middle::cstore;
 
-use syntax::ast::{self, IntTy, UintTy};
+use syntax::ast::{self, IntTy, UintTy, MetaItemKind};
 use syntax::codemap::{FileName, FilePathMapping};
 use syntax::edition::{Edition, EDITION_NAME_LIST, DEFAULT_EDITION};
 use syntax::parse::token;
@@ -1746,22 +1746,33 @@ pub fn parse_cfgspecs(cfgspecs: Vec<String>) -> ast::CrateConfig {
             let mut parser =
                 parse::new_parser_from_source_str(&sess, FileName::CfgSpec, s.to_string());
 
-            let meta_item = panictry!(parser.parse_meta_item());
+            macro_rules! error {($reason: expr) => {
+                early_error(ErrorOutputType::default(),
+                            &format!(concat!("invalid `--cfg` argument: `{}` (", $reason, ")"), s));
+            }}
 
-            if parser.token != token::Eof {
-                early_error(
-                    ErrorOutputType::default(),
-                    &format!("invalid --cfg argument: {}", s),
-                )
-            } else if meta_item.is_meta_item_list() {
-                let msg = format!(
-                    "invalid predicate in --cfg command line argument: `{}`",
-                    meta_item.ident
-                );
-                early_error(ErrorOutputType::default(), &msg)
+            match &mut parser.parse_meta_item() {
+                Ok(meta_item) if parser.token == token::Eof => {
+                    if meta_item.ident.segments.len() != 1 {
+                        error!("argument key must be an identifier");
+                    }
+                    match &meta_item.node {
+                        MetaItemKind::List(..) => {
+                            error!(r#"expected `key` or `key="value"`"#);
+                        }
+                        MetaItemKind::NameValue(lit) if !lit.node.is_str() => {
+                            error!("argument value must be a string");
+                        }
+                        MetaItemKind::NameValue(..) | MetaItemKind::Word => {
+                            return (meta_item.name(), meta_item.value_str());
+                        }
+                    }
+                }
+                Ok(..) => {}
+                Err(err) => err.cancel(),
             }
 
-            (meta_item.name(), meta_item.value_str())
+            error!(r#"expected `key` or `key="value"`"#);
         })
         .collect::<ast::CrateConfig>()
 }
diff --git a/src/libsyntax/attr/builtin.rs b/src/libsyntax/attr/builtin.rs
index ecd52a62eab26..da11c3d652ce5 100644
--- a/src/libsyntax/attr/builtin.rs
+++ b/src/libsyntax/attr/builtin.rs
@@ -435,7 +435,21 @@ pub fn cfg_matches(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Feat
         if let (Some(feats), Some(gated_cfg)) = (features, GatedCfg::gate(cfg)) {
             gated_cfg.check_and_emit(sess, feats);
         }
-        sess.config.contains(&(cfg.name(), cfg.value_str()))
+        let error = |span, msg| { sess.span_diagnostic.span_err(span, msg); true };
+        if cfg.ident.segments.len() != 1 {
+            return error(cfg.ident.span, "`cfg` predicate key must be an identifier");
+        }
+        match &cfg.node {
+            MetaItemKind::List(..) => {
+                error(cfg.span, "unexpected parentheses after `cfg` predicate key")
+            }
+            MetaItemKind::NameValue(lit) if !lit.node.is_str() => {
+                error(lit.span, "literal in `cfg` predicate value must be a string")
+            }
+            MetaItemKind::NameValue(..) | MetaItemKind::Word => {
+                sess.config.contains(&(cfg.name(), cfg.value_str()))
+            }
+        }
     })
 }
 
diff --git a/src/libsyntax/config.rs b/src/libsyntax/config.rs
index 3364378913952..d6ecf87a65dbe 100644
--- a/src/libsyntax/config.rs
+++ b/src/libsyntax/config.rs
@@ -123,25 +123,45 @@ impl<'a> StripUnconfigured<'a> {
                 return false;
             }
 
-            let mis = if !is_cfg(attr) {
+            if !is_cfg(attr) {
                 return true;
-            } else if let Some(mis) = attr.meta_item_list() {
-                mis
+            }
+
+            let error = |span, msg, suggestion: &str| {
+                let mut err = self.sess.span_diagnostic.struct_span_err(span, msg);
+                if !suggestion.is_empty() {
+                    err.span_suggestion(span, "expected syntax is", suggestion.into());
+                }
+                err.emit();
+                true
+            };
+
+            let meta_item = if let Some(meta_item) = attr.meta() {
+                meta_item
             } else {
-                return true;
+                // Not a well-formed meta-item. Why? We don't know.
+                return error(attr.span, "`cfg` is not a well-formed meta-item",
+                                        "#[cfg(/* predicate */)]");
+            };
+            let nested_meta_items = if let Some(nested_meta_items) = meta_item.meta_item_list() {
+                nested_meta_items
+            } else {
+                return error(meta_item.span, "`cfg` is not followed by parentheses",
+                                             "cfg(/* predicate */)");
             };
 
-            if mis.len() != 1 {
-                self.sess.span_diagnostic.span_err(attr.span, "expected 1 cfg-pattern");
-                return true;
+            if nested_meta_items.is_empty() {
+                return error(meta_item.span, "`cfg` predicate is not specified", "");
+            } else if nested_meta_items.len() > 1 {
+                return error(nested_meta_items.last().unwrap().span,
+                             "multiple `cfg` predicates are specified", "");
             }
 
-            if !mis[0].is_meta_item() {
-                self.sess.span_diagnostic.span_err(mis[0].span, "unexpected literal");
-                return true;
+            match nested_meta_items[0].meta_item() {
+                Some(meta_item) => attr::cfg_matches(meta_item, self.sess, self.features),
+                None => error(nested_meta_items[0].span,
+                              "`cfg` predicate key cannot be a literal", ""),
             }
-
-            attr::cfg_matches(mis[0].meta_item().unwrap(), self.sess, self.features)
         })
     }
 
diff --git a/src/test/compile-fail/cfg-empty-codemap.rs b/src/test/compile-fail/cfg-empty-codemap.rs
index 8868a5a95498e..be4989dddc509 100644
--- a/src/test/compile-fail/cfg-empty-codemap.rs
+++ b/src/test/compile-fail/cfg-empty-codemap.rs
@@ -12,7 +12,7 @@
 
 // compile-flags: --cfg ""
 
-// error-pattern: expected identifier, found
+// error-pattern: invalid `--cfg` argument
 
 pub fn main() {
 }
diff --git a/src/test/ui/cfg-arg-invalid-1.rs b/src/test/ui/cfg-arg-invalid-1.rs
new file mode 100644
index 0000000000000..690a415c0d4a7
--- /dev/null
+++ b/src/test/ui/cfg-arg-invalid-1.rs
@@ -0,0 +1,13 @@
+// Copyright 2018 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: --cfg a(b=c)
+// error-pattern: invalid `--cfg` argument: `a(b=c)` (expected `key` or `key="value"`)
+fn main() {}
diff --git a/src/test/compile-fail/cfg-arg-invalid.rs b/src/test/ui/cfg-arg-invalid-2.rs
similarity index 74%
rename from src/test/compile-fail/cfg-arg-invalid.rs
rename to src/test/ui/cfg-arg-invalid-2.rs
index 404630399c601..679057bb7024d 100644
--- a/src/test/compile-fail/cfg-arg-invalid.rs
+++ b/src/test/ui/cfg-arg-invalid-2.rs
@@ -1,4 +1,4 @@
-// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
 // file at the top-level directory of this distribution and at
 // http://rust-lang.org/COPYRIGHT.
 //
@@ -9,5 +9,5 @@
 // except according to those terms.
 
 // compile-flags: --cfg a{b}
-// error-pattern: invalid --cfg argument: a{b}
+// error-pattern: invalid `--cfg` argument: `a{b}` (expected `key` or `key="value"`)
 fn main() {}
diff --git a/src/test/ui/cfg-arg-invalid-3.rs b/src/test/ui/cfg-arg-invalid-3.rs
new file mode 100644
index 0000000000000..432a00a4babb2
--- /dev/null
+++ b/src/test/ui/cfg-arg-invalid-3.rs
@@ -0,0 +1,13 @@
+// Copyright 2018 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: --cfg a::b
+// error-pattern: invalid `--cfg` argument: `a::b` (argument key must be an identifier)
+fn main() {}
diff --git a/src/test/ui/issue-31495.rs b/src/test/ui/cfg-arg-invalid-4.rs
similarity index 70%
rename from src/test/ui/issue-31495.rs
rename to src/test/ui/cfg-arg-invalid-4.rs
index 794b8bb86bb57..b7ac7ea39a206 100644
--- a/src/test/ui/issue-31495.rs
+++ b/src/test/ui/cfg-arg-invalid-4.rs
@@ -1,4 +1,4 @@
-// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
 // file at the top-level directory of this distribution and at
 // http://rust-lang.org/COPYRIGHT.
 //
@@ -8,6 +8,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// compile-flags: --cfg foo(bar)
-// error-pattern: invalid predicate in --cfg command line argument: `foo`
+// compile-flags: --cfg a(b)
+// error-pattern: invalid `--cfg` argument: `a(b)` (expected `key` or `key="value"`)
 fn main() {}
diff --git a/src/test/ui/cfg-arg-invalid-5.rs b/src/test/ui/cfg-arg-invalid-5.rs
new file mode 100644
index 0000000000000..bb326e64b8d56
--- /dev/null
+++ b/src/test/ui/cfg-arg-invalid-5.rs
@@ -0,0 +1,13 @@
+// Copyright 2018 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: --cfg a=10
+// error-pattern: invalid `--cfg` argument: `a=10` (argument value must be a string)
+fn main() {}
diff --git a/src/test/ui/cfg-attr-syntax-validation.rs b/src/test/ui/cfg-attr-syntax-validation.rs
new file mode 100644
index 0000000000000..0e283d88cbbf8
--- /dev/null
+++ b/src/test/ui/cfg-attr-syntax-validation.rs
@@ -0,0 +1,42 @@
+// Copyright 2018 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#[cfg] //~ ERROR `cfg` is not followed by parentheses
+struct S1;
+
+#[cfg = 10] //~ ERROR `cfg` is not followed by parentheses
+struct S2;
+
+#[cfg()] //~ ERROR `cfg` predicate is not specified
+struct S3;
+
+#[cfg(a, b)] //~ ERROR multiple `cfg` predicates are specified
+struct S4;
+
+#[cfg("str")] //~ ERROR `cfg` predicate key cannot be a literal
+struct S5;
+
+#[cfg(a::b)] //~ ERROR `cfg` predicate key must be an identifier
+struct S6;
+
+#[cfg(a())] //~ ERROR invalid predicate `a`
+struct S7;
+
+#[cfg(a = 10)] //~ ERROR literal in `cfg` predicate value must be a string
+struct S8;
+
+macro_rules! generate_s9 {
+    ($expr: expr) => {
+        #[cfg(feature = $expr)] //~ ERROR `cfg` is not a well-formed meta-item
+        struct S9;
+    }
+}
+
+generate_s9!(concat!("nonexistent"));
diff --git a/src/test/ui/cfg-attr-syntax-validation.stderr b/src/test/ui/cfg-attr-syntax-validation.stderr
new file mode 100644
index 0000000000000..9c8a05d1092d8
--- /dev/null
+++ b/src/test/ui/cfg-attr-syntax-validation.stderr
@@ -0,0 +1,60 @@
+error: `cfg` is not followed by parentheses
+  --> $DIR/cfg-attr-syntax-validation.rs:11:1
+   |
+LL | #[cfg] //~ ERROR `cfg` is not followed by parentheses
+   | ^^^^^^ help: expected syntax is: `cfg(/* predicate */)`
+
+error: `cfg` is not followed by parentheses
+  --> $DIR/cfg-attr-syntax-validation.rs:14:1
+   |
+LL | #[cfg = 10] //~ ERROR `cfg` is not followed by parentheses
+   | ^^^^^^^^^^^ help: expected syntax is: `cfg(/* predicate */)`
+
+error: `cfg` predicate is not specified
+  --> $DIR/cfg-attr-syntax-validation.rs:17:1
+   |
+LL | #[cfg()] //~ ERROR `cfg` predicate is not specified
+   | ^^^^^^^^
+
+error: multiple `cfg` predicates are specified
+  --> $DIR/cfg-attr-syntax-validation.rs:20:10
+   |
+LL | #[cfg(a, b)] //~ ERROR multiple `cfg` predicates are specified
+   |          ^
+
+error: `cfg` predicate key cannot be a literal
+  --> $DIR/cfg-attr-syntax-validation.rs:23:7
+   |
+LL | #[cfg("str")] //~ ERROR `cfg` predicate key cannot be a literal
+   |       ^^^^^
+
+error: `cfg` predicate key must be an identifier
+  --> $DIR/cfg-attr-syntax-validation.rs:26:7
+   |
+LL | #[cfg(a::b)] //~ ERROR `cfg` predicate key must be an identifier
+   |       ^^^^
+
+error[E0537]: invalid predicate `a`
+  --> $DIR/cfg-attr-syntax-validation.rs:29:7
+   |
+LL | #[cfg(a())] //~ ERROR invalid predicate `a`
+   |       ^^^
+
+error: literal in `cfg` predicate value must be a string
+  --> $DIR/cfg-attr-syntax-validation.rs:32:11
+   |
+LL | #[cfg(a = 10)] //~ ERROR literal in `cfg` predicate value must be a string
+   |           ^^
+
+error: `cfg` is not a well-formed meta-item
+  --> $DIR/cfg-attr-syntax-validation.rs:37:9
+   |
+LL |         #[cfg(feature = $expr)] //~ ERROR `cfg` is not a well-formed meta-item
+   |         ^^^^^^^^^^^^^^^^^^^^^^^ help: expected syntax is: `#[cfg(/* predicate */)]`
+...
+LL | generate_s9!(concat!("nonexistent"));
+   | ------------------------------------- in this macro invocation
+
+error: aborting due to 9 previous errors
+
+For more information about this error, try `rustc --explain E0537`.