]> git.lizzy.rs Git - rust.git/blobdiff - src/libsyntax/feature_gate.rs
Rollup merge of #57407 - mehcode:stabilize-extern-crate-self, r=Centril
[rust.git] / src / libsyntax / feature_gate.rs
index ac20a62f11787aeabd0ef8e83195ca5164dbbf68..2820924824697e4fd27d732d0067075c912d4568 100644 (file)
 use rustc_target::spec::abi::Abi;
 use ast::{self, NodeId, PatKind, RangeEnd};
 use attr;
+use early_buffered_lints::BufferedEarlyLintId;
 use source_map::Spanned;
 use edition::{ALL_EDITIONS, Edition};
 use syntax_pos::{Span, DUMMY_SP};
 use errors::{DiagnosticBuilder, Handler};
 use visit::{self, FnKind, Visitor};
 use parse::ParseSess;
-use symbol::{keywords, Symbol};
+use symbol::Symbol;
 
-use std::{env};
+use std::env;
 
 macro_rules! set {
     ($field: ident) => {{
@@ -123,7 +124,6 @@ pub fn walk_feature_fields<F>(&self, mut f: F)
 
     (active, link_llvm_intrinsics, "1.0.0", Some(29602), None),
     (active, linkage, "1.0.0", Some(29603), None),
-    (active, quote, "1.0.0", Some(29601), None),
 
     // rustc internal
     (active, rustc_diagnostic_macros, "1.0.0", None, None),
@@ -372,9 +372,6 @@ pub fn walk_feature_fields<F>(&self, mut f: F)
     // Generic associated types (RFC 1598)
     (active, generic_associated_types, "1.23.0", Some(44265), None),
 
-    // `extern` in paths
-    (active, extern_in_paths, "1.23.0", Some(55600), None),
-
     // Infer static outlives requirements (RFC 2093).
     (active, infer_static_outlives_requirements, "1.26.0", Some(54185), None),
 
@@ -456,14 +453,14 @@ pub fn walk_feature_fields<F>(&self, mut f: F)
     // Adds `reason` and `expect` lint attributes.
     (active, lint_reasons, "1.31.0", Some(54503), None),
 
-    // `extern crate self as foo;` puts local crate root into extern prelude under name `foo`.
-    (active, extern_crate_self, "1.31.0", Some(56409), None),
-
     // Allows paths to enum variants on type aliases.
     (active, type_alias_enum_variants, "1.31.0", Some(49683), None),
 
     // Re-Rebalance coherence
     (active, re_rebalance_coherence, "1.32.0", Some(55437), None),
+
+    // #[optimize(X)]
+    (active, optimize_attribute, "1.34.0", Some(54882), None),
 );
 
 declare_features! (
@@ -503,6 +500,10 @@ pub fn walk_feature_fields<F>(&self, mut f: F)
     // Allows the use of `#[derive(Anything)]` as sugar for `#[derive_Anything]`.
     (removed, custom_derive, "1.0.0", Some(29644), None,
      Some("subsumed by `#[proc_macro_derive]`")),
+    // Paths of the form: `extern::foo::bar`
+    (removed, extern_in_paths, "1.33.0", Some(55600), None,
+     Some("subsumed by `::foo::bar` paths")),
+    (removed, quote, "1.0.0", Some(29601), None, None),
 );
 
 declare_features! (
@@ -685,6 +686,8 @@ pub fn walk_feature_fields<F>(&self, mut f: F)
     (accepted, uniform_paths, "1.32.0", Some(53130), None),
     // Allows `cfg(target_vendor = "...")`.
     (accepted, cfg_target_vendor, "1.33.0", Some(29718), None),
+    // `extern crate self as foo;` puts local crate root into extern prelude under name `foo`.
+    (accepted, extern_crate_self, "1.34.0", Some(56409), None),
 );
 
 // If you change this, please modify `src/doc/unstable-book` as well. You must
@@ -715,6 +718,47 @@ pub enum AttributeGate {
     Ungated,
 }
 
+/// A template that the attribute input must match.
+/// Only top-level shape (`#[attr]` vs `#[attr(...)]` vs `#[attr = ...]`) is considered now.
+#[derive(Clone, Copy)]
+pub struct AttributeTemplate {
+    word: bool,
+    list: Option<&'static str>,
+    name_value_str: Option<&'static str>,
+}
+
+impl AttributeTemplate {
+    /// Check that the given meta-item is compatible with this template.
+    fn compatible(&self, meta_item_kind: &ast::MetaItemKind) -> bool {
+        match meta_item_kind {
+            ast::MetaItemKind::Word => self.word,
+            ast::MetaItemKind::List(..) => self.list.is_some(),
+            ast::MetaItemKind::NameValue(lit) if lit.node.is_str() => self.name_value_str.is_some(),
+            ast::MetaItemKind::NameValue(..) => false,
+        }
+    }
+}
+
+/// A convenience macro for constructing attribute templates.
+/// E.g. `template!(Word, List: "description")` means that the attribute
+/// supports forms `#[attr]` and `#[attr(description)]`.
+macro_rules! template {
+    (Word) => { template!(@ true, None, None) };
+    (List: $descr: expr) => { template!(@ false, Some($descr), None) };
+    (NameValueStr: $descr: expr) => { template!(@ false, None, Some($descr)) };
+    (Word, List: $descr: expr) => { template!(@ true, Some($descr), None) };
+    (Word, NameValueStr: $descr: expr) => { template!(@ true, None, Some($descr)) };
+    (List: $descr1: expr, NameValueStr: $descr2: expr) => {
+        template!(@ false, Some($descr1), Some($descr2))
+    };
+    (Word, List: $descr1: expr, NameValueStr: $descr2: expr) => {
+        template!(@ true, Some($descr1), Some($descr2))
+    };
+    (@ $word: expr, $list: expr, $name_value_str: expr) => { AttributeTemplate {
+        word: $word, list: $list, name_value_str: $name_value_str
+    } };
+}
+
 impl AttributeGate {
     fn is_deprecated(&self) -> bool {
         match *self {
@@ -752,225 +796,248 @@ fn f(features: &Features) -> bool {
     }}
 }
 
-pub fn deprecated_attributes() -> Vec<&'static (&'static str, AttributeType, AttributeGate)> {
-    BUILTIN_ATTRIBUTES.iter().filter(|a| a.2.is_deprecated()).collect()
+pub fn deprecated_attributes() -> Vec<&'static (&'static str, AttributeType,
+                                                AttributeTemplate, AttributeGate)> {
+    BUILTIN_ATTRIBUTES.iter().filter(|(.., gate)| gate.is_deprecated()).collect()
 }
 
 pub fn is_builtin_attr_name(name: ast::Name) -> bool {
-    BUILTIN_ATTRIBUTES.iter().any(|&(builtin_name, _, _)| name == builtin_name)
+    BUILTIN_ATTRIBUTES.iter().any(|&(builtin_name, ..)| name == builtin_name)
 }
 
 pub fn is_builtin_attr(attr: &ast::Attribute) -> bool {
-    BUILTIN_ATTRIBUTES.iter().any(|&(builtin_name, _, _)| attr.path == builtin_name)
+    BUILTIN_ATTRIBUTES.iter().any(|&(builtin_name, ..)| attr.path == builtin_name)
 }
 
 // Attributes that have a special meaning to rustc or rustdoc
-pub const BUILTIN_ATTRIBUTES: &[(&str, AttributeType, AttributeGate)] = &[
+pub const BUILTIN_ATTRIBUTES: &[(&str, AttributeType, AttributeTemplate, AttributeGate)] = &[
     // Normal attributes
 
-    ("warn", Normal, Ungated),
-    ("allow", Normal, Ungated),
-    ("forbid", Normal, Ungated),
-    ("deny", Normal, Ungated),
-
-    ("macro_use", Normal, Ungated),
-    ("macro_export", Normal, Ungated),
-    ("plugin_registrar", Normal, Ungated),
-
-    ("cfg", Normal, Ungated),
-    ("cfg_attr", Normal, Ungated),
-    ("main", Normal, Ungated),
-    ("start", Normal, Ungated),
-    ("repr", Normal, Ungated),
-    ("path", Normal, Ungated),
-    ("abi", Normal, Ungated),
-    ("automatically_derived", Normal, Ungated),
-    ("no_mangle", Normal, Ungated),
-    ("no_link", Normal, Ungated),
-    ("derive", Normal, Ungated),
-    ("should_panic", Normal, Ungated),
-    ("ignore", Normal, Ungated),
-    ("no_implicit_prelude", Normal, Ungated),
-    ("reexport_test_harness_main", Normal, Ungated),
-    ("link_args", Normal, Gated(Stability::Unstable,
+    ("warn", Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), Ungated),
+    ("allow", Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), Ungated),
+    ("forbid", Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), Ungated),
+    ("deny", Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), Ungated),
+
+    ("macro_use", Normal, template!(Word, List: "name1, name2, ..."), Ungated),
+    ("macro_export", Normal, template!(Word, List: "local_inner_macros"), Ungated),
+    ("plugin_registrar", Normal, template!(Word), Ungated),
+
+    ("cfg", Normal, template!(List: "predicate"), Ungated),
+    ("cfg_attr", Normal, template!(List: "predicate, attr1, attr2, ..."), Ungated),
+    ("main", Normal, template!(Word), Ungated),
+    ("start", Normal, template!(Word), Ungated),
+    ("repr", Normal, template!(List: "C, packed, ..."), Ungated),
+    ("path", Normal, template!(NameValueStr: "file"), Ungated),
+    ("automatically_derived", Normal, template!(Word), Ungated),
+    ("no_mangle", Normal, template!(Word), Ungated),
+    ("no_link", Normal, template!(Word), Ungated),
+    ("derive", Normal, template!(List: "Trait1, Trait2, ..."), Ungated),
+    ("should_panic", Normal, template!(Word, List: r#"expected = "reason"#, NameValueStr: "reason"),
+                                Ungated),
+    ("ignore", Normal, template!(Word, NameValueStr: "reason"), Ungated),
+    ("no_implicit_prelude", Normal, template!(Word), Ungated),
+    ("reexport_test_harness_main", Normal, template!(NameValueStr: "name"), Ungated),
+    ("link_args", Normal, template!(NameValueStr: "args"), Gated(Stability::Unstable,
                                 "link_args",
                                 "the `link_args` attribute is experimental and not \
                                  portable across platforms, it is recommended to \
                                  use `#[link(name = \"foo\")] instead",
                                 cfg_fn!(link_args))),
-    ("macro_escape", Normal, Ungated),
+    ("macro_escape", Normal, template!(Word), Ungated),
 
     // RFC #1445.
-    ("structural_match", Whitelisted, Gated(Stability::Unstable,
+    ("structural_match", Whitelisted, template!(Word), Gated(Stability::Unstable,
                                             "structural_match",
                                             "the semantics of constant patterns is \
                                              not yet settled",
                                             cfg_fn!(structural_match))),
 
     // RFC #2008
-    ("non_exhaustive", Whitelisted, Gated(Stability::Unstable,
+    ("non_exhaustive", Whitelisted, template!(Word), Gated(Stability::Unstable,
                                           "non_exhaustive",
                                           "non exhaustive is an experimental feature",
                                           cfg_fn!(non_exhaustive))),
 
     // RFC #1268
-    ("marker", Normal, Gated(Stability::Unstable,
+    ("marker", Normal, template!(Word), Gated(Stability::Unstable,
                              "marker_trait_attr",
                              "marker traits is an experimental feature",
                              cfg_fn!(marker_trait_attr))),
 
-    ("plugin", CrateLevel, Gated(Stability::Unstable,
+    ("plugin", CrateLevel, template!(List: "name|name(args)"), Gated(Stability::Unstable,
                                  "plugin",
                                  "compiler plugins are experimental \
                                   and possibly buggy",
                                  cfg_fn!(plugin))),
 
-    ("no_std", CrateLevel, Ungated),
-    ("no_core", CrateLevel, Gated(Stability::Unstable,
+    ("no_std", CrateLevel, template!(Word), Ungated),
+    ("no_core", CrateLevel, template!(Word), Gated(Stability::Unstable,
                                   "no_core",
                                   "no_core is experimental",
                                   cfg_fn!(no_core))),
-    ("lang", Normal, Gated(Stability::Unstable,
+    ("lang", Normal, template!(NameValueStr: "name"), Gated(Stability::Unstable,
                            "lang_items",
                            "language items are subject to change",
                            cfg_fn!(lang_items))),
-    ("linkage", Whitelisted, Gated(Stability::Unstable,
+    ("linkage", Whitelisted, template!(NameValueStr: "external|internal|..."),
+                                   Gated(Stability::Unstable,
                                    "linkage",
                                    "the `linkage` attribute is experimental \
                                     and not portable across platforms",
                                    cfg_fn!(linkage))),
-    ("thread_local", Whitelisted, Gated(Stability::Unstable,
+    ("thread_local", Whitelisted, template!(Word), Gated(Stability::Unstable,
                                         "thread_local",
                                         "`#[thread_local]` is an experimental feature, and does \
                                          not currently handle destructors.",
                                         cfg_fn!(thread_local))),
 
-    ("rustc_on_unimplemented", Normal, Gated(Stability::Unstable,
+    ("rustc_on_unimplemented", Normal, template!(List:
+                          r#"/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...""#,
+                          NameValueStr: "message"),
+                                             Gated(Stability::Unstable,
                                              "on_unimplemented",
                                              "the `#[rustc_on_unimplemented]` attribute \
                                               is an experimental feature",
                                              cfg_fn!(on_unimplemented))),
-    ("rustc_const_unstable", Normal, Gated(Stability::Unstable,
+    ("rustc_const_unstable", Normal, template!(List: r#"feature = "name""#),
+                                             Gated(Stability::Unstable,
                                              "rustc_const_unstable",
                                              "the `#[rustc_const_unstable]` attribute \
                                               is an internal feature",
                                              cfg_fn!(rustc_const_unstable))),
-    ("global_allocator", Normal, Ungated),
-    ("default_lib_allocator", Whitelisted, Gated(Stability::Unstable,
+    ("global_allocator", Normal, template!(Word), Ungated),
+    ("default_lib_allocator", Whitelisted, template!(Word), Gated(Stability::Unstable,
                                             "allocator_internals",
                                             "the `#[default_lib_allocator]` \
                                              attribute is an experimental feature",
                                             cfg_fn!(allocator_internals))),
-    ("needs_allocator", Normal, Gated(Stability::Unstable,
+    ("needs_allocator", Normal, template!(Word), Gated(Stability::Unstable,
                                       "allocator_internals",
                                       "the `#[needs_allocator]` \
                                        attribute is an experimental \
                                        feature",
                                       cfg_fn!(allocator_internals))),
-    ("panic_runtime", Whitelisted, Gated(Stability::Unstable,
+    ("panic_runtime", Whitelisted, template!(Word), Gated(Stability::Unstable,
                                          "panic_runtime",
                                          "the `#[panic_runtime]` attribute is \
                                           an experimental feature",
                                          cfg_fn!(panic_runtime))),
-    ("needs_panic_runtime", Whitelisted, Gated(Stability::Unstable,
+    ("needs_panic_runtime", Whitelisted, template!(Word), Gated(Stability::Unstable,
                                                "needs_panic_runtime",
                                                "the `#[needs_panic_runtime]` \
                                                 attribute is an experimental \
                                                 feature",
                                                cfg_fn!(needs_panic_runtime))),
-    ("rustc_outlives", Normal, Gated(Stability::Unstable,
+    ("rustc_outlives", Normal, template!(Word), Gated(Stability::Unstable,
                                      "rustc_attrs",
                                      "the `#[rustc_outlives]` attribute \
                                       is just used for rustc unit tests \
                                       and will never be stable",
                                      cfg_fn!(rustc_attrs))),
-    ("rustc_variance", Normal, Gated(Stability::Unstable,
+    ("rustc_variance", Normal, template!(Word), Gated(Stability::Unstable,
                                      "rustc_attrs",
                                      "the `#[rustc_variance]` attribute \
                                       is just used for rustc unit tests \
                                       and will never be stable",
                                      cfg_fn!(rustc_attrs))),
-    ("rustc_regions", Normal, Gated(Stability::Unstable,
+    ("rustc_layout", Normal, template!(List: "field1, field2, ..."),
+     Gated(Stability::Unstable,
+           "rustc_attrs",
+           "the `#[rustc_layout]` attribute \
+            is just used for rustc unit tests \
+            and will never be stable",
+           cfg_fn!(rustc_attrs))),
+    ("rustc_regions", Normal, template!(Word), Gated(Stability::Unstable,
                                     "rustc_attrs",
                                     "the `#[rustc_regions]` attribute \
                                      is just used for rustc unit tests \
                                      and will never be stable",
                                     cfg_fn!(rustc_attrs))),
-    ("rustc_error", Whitelisted, Gated(Stability::Unstable,
+    ("rustc_error", Whitelisted, template!(Word), Gated(Stability::Unstable,
                                        "rustc_attrs",
                                        "the `#[rustc_error]` attribute \
                                         is just used for rustc unit tests \
                                         and will never be stable",
                                        cfg_fn!(rustc_attrs))),
-    ("rustc_dump_user_substs", Whitelisted, Gated(Stability::Unstable,
+    ("rustc_dump_user_substs", Whitelisted, template!(Word), Gated(Stability::Unstable,
                                        "rustc_attrs",
                                        "this attribute \
                                         is just used for rustc unit tests \
                                         and will never be stable",
                                        cfg_fn!(rustc_attrs))),
-    ("rustc_if_this_changed", Whitelisted, Gated(Stability::Unstable,
+    ("rustc_if_this_changed", Whitelisted, template!(Word, List: "DepNode"),
+                                                 Gated(Stability::Unstable,
                                                  "rustc_attrs",
                                                  "the `#[rustc_if_this_changed]` attribute \
                                                   is just used for rustc unit tests \
                                                   and will never be stable",
                                                  cfg_fn!(rustc_attrs))),
-    ("rustc_then_this_would_need", Whitelisted, Gated(Stability::Unstable,
+    ("rustc_then_this_would_need", Whitelisted, template!(List: "DepNode"),
+                                                      Gated(Stability::Unstable,
                                                       "rustc_attrs",
                                                       "the `#[rustc_if_this_changed]` attribute \
                                                        is just used for rustc unit tests \
                                                        and will never be stable",
                                                       cfg_fn!(rustc_attrs))),
-    ("rustc_dirty", Whitelisted, Gated(Stability::Unstable,
+    ("rustc_dirty", Whitelisted, template!(List: r#"cfg = "...", /*opt*/ label = "...",
+                                                    /*opt*/ except = "...""#),
+                                       Gated(Stability::Unstable,
                                        "rustc_attrs",
                                        "the `#[rustc_dirty]` attribute \
                                         is just used for rustc unit tests \
                                         and will never be stable",
                                        cfg_fn!(rustc_attrs))),
-    ("rustc_clean", Whitelisted, Gated(Stability::Unstable,
+    ("rustc_clean", Whitelisted, template!(List: r#"cfg = "...", /*opt*/ label = "...",
+                                                    /*opt*/ except = "...""#),
+                                       Gated(Stability::Unstable,
                                        "rustc_attrs",
                                        "the `#[rustc_clean]` attribute \
                                         is just used for rustc unit tests \
                                         and will never be stable",
                                        cfg_fn!(rustc_attrs))),
-    ("rustc_partition_reused", Whitelisted, Gated(Stability::Unstable,
+    ("rustc_partition_reused", Whitelisted, template!(List: r#"cfg = "...", module = "...""#),
+                                                  Gated(Stability::Unstable,
                                                   "rustc_attrs",
                                                   "this attribute \
                                                    is just used for rustc unit tests \
                                                    and will never be stable",
                                                   cfg_fn!(rustc_attrs))),
-    ("rustc_partition_codegened", Whitelisted, Gated(Stability::Unstable,
+    ("rustc_partition_codegened", Whitelisted, template!(List: r#"cfg = "...", module = "...""#),
+                                                      Gated(Stability::Unstable,
                                                       "rustc_attrs",
                                                       "this attribute \
                                                        is just used for rustc unit tests \
                                                        and will never be stable",
                                                       cfg_fn!(rustc_attrs))),
-    ("rustc_expected_cgu_reuse", Whitelisted, Gated(Stability::Unstable,
+    ("rustc_expected_cgu_reuse", Whitelisted, template!(List: r#"cfg = "...", module = "...",
+                                                              kind = "...""#),
+                                                    Gated(Stability::Unstable,
                                                     "rustc_attrs",
                                                     "this attribute \
                                                      is just used for rustc unit tests \
                                                      and will never be stable",
                                                     cfg_fn!(rustc_attrs))),
-    ("rustc_synthetic", Whitelisted, Gated(Stability::Unstable,
+    ("rustc_synthetic", Whitelisted, template!(Word), Gated(Stability::Unstable,
                                                       "rustc_attrs",
                                                       "this attribute \
                                                        is just used for rustc unit tests \
                                                        and will never be stable",
                                                       cfg_fn!(rustc_attrs))),
-    ("rustc_symbol_name", Whitelisted, Gated(Stability::Unstable,
+    ("rustc_symbol_name", Whitelisted, template!(Word), Gated(Stability::Unstable,
                                              "rustc_attrs",
                                              "internal rustc attributes will never be stable",
                                              cfg_fn!(rustc_attrs))),
-    ("rustc_item_path", Whitelisted, Gated(Stability::Unstable,
+    ("rustc_item_path", Whitelisted, template!(Word), Gated(Stability::Unstable,
                                            "rustc_attrs",
                                            "internal rustc attributes will never be stable",
                                            cfg_fn!(rustc_attrs))),
-    ("rustc_mir", Whitelisted, Gated(Stability::Unstable,
+    ("rustc_mir", Whitelisted, template!(List: "arg1, arg2, ..."), Gated(Stability::Unstable,
                                      "rustc_attrs",
                                      "the `#[rustc_mir]` attribute \
                                       is just used for rustc unit tests \
                                       and will never be stable",
                                      cfg_fn!(rustc_attrs))),
-    ("rustc_inherit_overflow_checks", Whitelisted, Gated(Stability::Unstable,
+    ("rustc_inherit_overflow_checks", Whitelisted, template!(Word), Gated(Stability::Unstable,
                                                          "rustc_attrs",
                                                          "the `#[rustc_inherit_overflow_checks]` \
                                                           attribute is just used to control \
@@ -979,41 +1046,35 @@ pub fn is_builtin_attr(attr: &ast::Attribute) -> bool {
                                                           across crates and will never be stable",
                                                           cfg_fn!(rustc_attrs))),
 
-    ("rustc_dump_program_clauses", Whitelisted, Gated(Stability::Unstable,
+    ("rustc_dump_program_clauses", Whitelisted, template!(Word), Gated(Stability::Unstable,
                                                      "rustc_attrs",
                                                      "the `#[rustc_dump_program_clauses]` \
                                                       attribute is just used for rustc unit \
                                                       tests and will never be stable",
                                                      cfg_fn!(rustc_attrs))),
-    ("rustc_test_marker", Normal, Gated(Stability::Unstable,
+    ("rustc_test_marker", Normal, template!(Word), Gated(Stability::Unstable,
                                      "rustc_attrs",
                                      "the `#[rustc_test_marker]` attribute \
                                       is used internally to track tests",
                                      cfg_fn!(rustc_attrs))),
-    ("rustc_transparent_macro", Whitelisted, Gated(Stability::Unstable,
+    ("rustc_transparent_macro", Whitelisted, template!(Word), Gated(Stability::Unstable,
                                                    "rustc_attrs",
                                                    "used internally for testing macro hygiene",
                                                     cfg_fn!(rustc_attrs))),
-
-    // RFC #2094
-    ("nll", Whitelisted, Gated(Stability::Unstable,
-                               "nll",
-                               "Non lexical lifetimes",
-                               cfg_fn!(nll))),
-    ("compiler_builtins", Whitelisted, Gated(Stability::Unstable,
+    ("compiler_builtins", Whitelisted, template!(Word), Gated(Stability::Unstable,
                                              "compiler_builtins",
                                              "the `#[compiler_builtins]` attribute is used to \
                                               identify the `compiler_builtins` crate which \
                                               contains compiler-rt intrinsics and will never be \
                                               stable",
                                           cfg_fn!(compiler_builtins))),
-    ("sanitizer_runtime", Whitelisted, Gated(Stability::Unstable,
+    ("sanitizer_runtime", Whitelisted, template!(Word), Gated(Stability::Unstable,
                                              "sanitizer_runtime",
                                              "the `#[sanitizer_runtime]` attribute is used to \
                                               identify crates that contain the runtime of a \
                                               sanitizer and will never be stable",
                                              cfg_fn!(sanitizer_runtime))),
-    ("profiler_runtime", Whitelisted, Gated(Stability::Unstable,
+    ("profiler_runtime", Whitelisted, template!(Word), Gated(Stability::Unstable,
                                              "profiler_runtime",
                                              "the `#[profiler_runtime]` attribute is used to \
                                               identify the `profiler_builtins` crate which \
@@ -1021,55 +1082,58 @@ pub fn is_builtin_attr(attr: &ast::Attribute) -> bool {
                                               stable",
                                              cfg_fn!(profiler_runtime))),
 
-    ("allow_internal_unstable", Normal, Gated(Stability::Unstable,
+    ("allow_internal_unstable", Normal, template!(Word), Gated(Stability::Unstable,
                                               "allow_internal_unstable",
                                               EXPLAIN_ALLOW_INTERNAL_UNSTABLE,
                                               cfg_fn!(allow_internal_unstable))),
 
-    ("allow_internal_unsafe", Normal, Gated(Stability::Unstable,
+    ("allow_internal_unsafe", Normal, template!(Word), Gated(Stability::Unstable,
                                             "allow_internal_unsafe",
                                             EXPLAIN_ALLOW_INTERNAL_UNSAFE,
                                             cfg_fn!(allow_internal_unsafe))),
 
-    ("fundamental", Whitelisted, Gated(Stability::Unstable,
+    ("fundamental", Whitelisted, template!(Word), Gated(Stability::Unstable,
                                        "fundamental",
                                        "the `#[fundamental]` attribute \
                                         is an experimental feature",
                                        cfg_fn!(fundamental))),
 
-    ("proc_macro_derive", Normal, Ungated),
+    ("proc_macro_derive", Normal, template!(List: "TraitName, \
+                                                   /*opt*/ attributes(name1, name2, ...)"),
+                                       Ungated),
 
-    ("rustc_copy_clone_marker", Whitelisted, Gated(Stability::Unstable,
+    ("rustc_copy_clone_marker", Whitelisted, template!(Word), Gated(Stability::Unstable,
                                                    "rustc_attrs",
                                                    "internal implementation detail",
                                                    cfg_fn!(rustc_attrs))),
 
     // FIXME: #14408 whitelist docs since rustdoc looks at them
-    ("doc", Whitelisted, Ungated),
+    ("doc", Whitelisted, template!(List: "hidden|inline|...", NameValueStr: "string"), Ungated),
 
     // FIXME: #14406 these are processed in codegen, which happens after the
     // lint pass
-    ("cold", Whitelisted, Ungated),
-    ("naked", Whitelisted, Gated(Stability::Unstable,
+    ("cold", Whitelisted, template!(Word), Ungated),
+    ("naked", Whitelisted, template!(Word), Gated(Stability::Unstable,
                                  "naked_functions",
                                  "the `#[naked]` attribute \
                                   is an experimental feature",
                                  cfg_fn!(naked_functions))),
-    ("target_feature", Whitelisted, Ungated),
-    ("export_name", Whitelisted, Ungated),
-    ("inline", Whitelisted, Ungated),
-    ("link", Whitelisted, Ungated),
-    ("link_name", Whitelisted, Ungated),
-    ("link_section", Whitelisted, Ungated),
-    ("no_builtins", Whitelisted, Ungated),
-    ("no_mangle", Whitelisted, Ungated),
-    ("no_debug", Whitelisted, Gated(
+    ("target_feature", Whitelisted, template!(List: r#"enable = "name""#), Ungated),
+    ("export_name", Whitelisted, template!(NameValueStr: "name"), Ungated),
+    ("inline", Whitelisted, template!(Word, List: "always|never"), Ungated),
+    ("link", Whitelisted, template!(List: r#"name = "...", /*opt*/ kind = "dylib|static|...",
+                                             /*opt*/ cfg = "...""#), Ungated),
+    ("link_name", Whitelisted, template!(NameValueStr: "name"), Ungated),
+    ("link_section", Whitelisted, template!(NameValueStr: "name"), Ungated),
+    ("no_builtins", Whitelisted, template!(Word), Ungated),
+    ("no_mangle", Whitelisted, template!(Word), Ungated),
+    ("no_debug", Whitelisted, template!(Word), Gated(
         Stability::Deprecated("https://github.com/rust-lang/rust/issues/29721", None),
         "no_debug",
         "the `#[no_debug]` attribute was an experimental feature that has been \
          deprecated due to lack of demand",
         cfg_fn!(no_debug))),
-    ("omit_gdb_pretty_printer_section", Whitelisted, Gated(Stability::Unstable,
+    ("omit_gdb_pretty_printer_section", Whitelisted, template!(Word), Gated(Stability::Unstable,
                                                        "omit_gdb_pretty_printer_section",
                                                        "the `#[omit_gdb_pretty_printer_section]` \
                                                         attribute is just used for the Rust test \
@@ -1077,6 +1141,7 @@ pub fn is_builtin_attr(attr: &ast::Attribute) -> bool {
                                                        cfg_fn!(omit_gdb_pretty_printer_section))),
     ("unsafe_destructor_blind_to_params",
      Normal,
+     template!(Word),
      Gated(Stability::Deprecated("https://github.com/rust-lang/rust/issues/34761",
                                  Some("replace this attribute with `#[may_dangle]`")),
            "dropck_parametricity",
@@ -1085,93 +1150,97 @@ pub fn is_builtin_attr(attr: &ast::Attribute) -> bool {
            cfg_fn!(dropck_parametricity))),
     ("may_dangle",
      Normal,
+     template!(Word),
      Gated(Stability::Unstable,
            "dropck_eyepatch",
            "may_dangle has unstable semantics and may be removed in the future",
            cfg_fn!(dropck_eyepatch))),
-    ("unwind", Whitelisted, Gated(Stability::Unstable,
+    ("unwind", Whitelisted, template!(List: "allowed"), Gated(Stability::Unstable,
                                   "unwind_attributes",
                                   "#[unwind] is experimental",
                                   cfg_fn!(unwind_attributes))),
-    ("used", Whitelisted, Ungated),
+    ("used", Whitelisted, template!(Word), Ungated),
 
     // used in resolve
-    ("prelude_import", Whitelisted, Gated(Stability::Unstable,
+    ("prelude_import", Whitelisted, template!(Word), Gated(Stability::Unstable,
                                           "prelude_import",
                                           "`#[prelude_import]` is for use by rustc only",
                                           cfg_fn!(prelude_import))),
 
     // FIXME: #14407 these are only looked at on-demand so we can't
     // guarantee they'll have already been checked
-    ("rustc_deprecated", Whitelisted, Ungated),
-    ("must_use", Whitelisted, Ungated),
-    ("stable", Whitelisted, Ungated),
-    ("unstable", Whitelisted, Ungated),
-    ("deprecated", Normal, Ungated),
-
-    ("rustc_paren_sugar", Normal, Gated(Stability::Unstable,
+    ("rustc_deprecated", Whitelisted, template!(List: r#"since = "version", reason = "...""#),
+                                        Ungated),
+    ("must_use", Whitelisted, template!(Word, NameValueStr: "reason"), Ungated),
+    ("stable", Whitelisted, template!(List: r#"feature = "name", since = "version""#), Ungated),
+    ("unstable", Whitelisted, template!(List: r#"feature = "name", reason = "...", issue = "N""#),
+                                        Ungated),
+    ("deprecated", Normal, template!(Word, List: r#"/*opt*/ since = "version",
+                                                    /*opt*/ note = "reason"#,
+                                                    NameValueStr: "reason"), Ungated),
+
+    ("rustc_paren_sugar", Normal, template!(Word), Gated(Stability::Unstable,
                                         "unboxed_closures",
                                         "unboxed_closures are still evolving",
                                         cfg_fn!(unboxed_closures))),
 
-    ("windows_subsystem", Whitelisted, Ungated),
+    ("windows_subsystem", Whitelisted, template!(NameValueStr: "windows|console"), Ungated),
 
-    ("proc_macro_attribute", Normal, Ungated),
-    ("proc_macro", Normal, Ungated),
+    ("proc_macro_attribute", Normal, template!(Word), Ungated),
+    ("proc_macro", Normal, template!(Word), Ungated),
 
-    ("rustc_proc_macro_decls", Normal, Gated(Stability::Unstable,
+    ("rustc_proc_macro_decls", Normal, template!(Word), Gated(Stability::Unstable,
                                              "rustc_proc_macro_decls",
                                              "used internally by rustc",
                                              cfg_fn!(rustc_attrs))),
 
-    ("allow_fail", Normal, Gated(Stability::Unstable,
+    ("allow_fail", Normal, template!(Word), Gated(Stability::Unstable,
                                  "allow_fail",
                                  "allow_fail attribute is currently unstable",
                                  cfg_fn!(allow_fail))),
 
-    ("rustc_std_internal_symbol", Whitelisted, Gated(Stability::Unstable,
+    ("rustc_std_internal_symbol", Whitelisted, template!(Word), Gated(Stability::Unstable,
                                      "rustc_attrs",
                                      "this is an internal attribute that will \
                                       never be stable",
                                      cfg_fn!(rustc_attrs))),
 
     // whitelists "identity-like" conversion methods to suggest on type mismatch
-    ("rustc_conversion_suggestion", Whitelisted, Gated(Stability::Unstable,
+    ("rustc_conversion_suggestion", Whitelisted, template!(Word), Gated(Stability::Unstable,
                                                        "rustc_attrs",
                                                        "this is an internal attribute that will \
                                                         never be stable",
                                                        cfg_fn!(rustc_attrs))),
 
-    ("rustc_args_required_const", Whitelisted, Gated(Stability::Unstable,
+    ("rustc_args_required_const", Whitelisted, template!(List: "N"), Gated(Stability::Unstable,
                                  "rustc_attrs",
                                  "never will be stable",
                                  cfg_fn!(rustc_attrs))),
-
-    // RFC #2093
-    ("infer_static_outlives_requirements", Normal, Gated(Stability::Unstable,
-                                   "infer_static_outlives_requirements",
-                                   "infer 'static lifetime requirements",
-                                   cfg_fn!(infer_static_outlives_requirements))),
-
     // RFC 2070
-    ("panic_handler", Normal, Ungated),
+    ("panic_handler", Normal, template!(Word), Ungated),
 
-    ("alloc_error_handler", Normal, Gated(Stability::Unstable,
+    ("alloc_error_handler", Normal, template!(Word), Gated(Stability::Unstable,
                            "alloc_error_handler",
                            "#[alloc_error_handler] is an unstable feature",
                            cfg_fn!(alloc_error_handler))),
 
+    // RFC 2412
+    ("optimize", Whitelisted, template!(List: "size|speed"), Gated(Stability::Unstable,
+                               "optimize_attribute",
+                               "#[optimize] attribute is an unstable feature",
+                               cfg_fn!(optimize_attribute))),
+
     // Crate level attributes
-    ("crate_name", CrateLevel, Ungated),
-    ("crate_type", CrateLevel, Ungated),
-    ("crate_id", CrateLevel, Ungated),
-    ("feature", CrateLevel, Ungated),
-    ("no_start", CrateLevel, Ungated),
-    ("no_main", CrateLevel, Ungated),
-    ("no_builtins", CrateLevel, Ungated),
-    ("recursion_limit", CrateLevel, Ungated),
-    ("type_length_limit", CrateLevel, Ungated),
-    ("test_runner", CrateLevel, Gated(Stability::Unstable,
+    ("crate_name", CrateLevel, template!(NameValueStr: "name"), Ungated),
+    ("crate_type", CrateLevel, template!(NameValueStr: "bin|lib|..."), Ungated),
+    ("crate_id", CrateLevel, template!(NameValueStr: "ignored"), Ungated),
+    ("feature", CrateLevel, template!(List: "name1, name1, ..."), Ungated),
+    ("no_start", CrateLevel, template!(Word), Ungated),
+    ("no_main", CrateLevel, template!(Word), Ungated),
+    ("no_builtins", CrateLevel, template!(Word), Ungated),
+    ("recursion_limit", CrateLevel, template!(NameValueStr: "N"), Ungated),
+    ("type_length_limit", CrateLevel, template!(NameValueStr: "N"), Ungated),
+    ("test_runner", CrateLevel, template!(List: "path"), Gated(Stability::Unstable,
                     "custom_test_frameworks",
                     EXPLAIN_CUSTOM_TEST_FRAMEWORKS,
                     cfg_fn!(custom_test_frameworks))),
@@ -1247,7 +1316,7 @@ impl<'a> Context<'a> {
     fn check_attribute(&self, attr: &ast::Attribute, is_macro: bool) {
         debug!("check_attribute(attr = {:?})", attr);
         let name = attr.name().as_str();
-        for &(n, ty, ref gateage) in BUILTIN_ATTRIBUTES {
+        for &(n, ty, _template, ref gateage) in BUILTIN_ATTRIBUTES {
             if name == n {
                 if let Gated(_, name, desc, ref has_feature) = *gateage {
                     gate_feature_fn!(self, has_feature, attr.span, name, desc, GateStrength::Hard);
@@ -1482,6 +1551,52 @@ fn check_abi(&self, abi: Abi, span: Span) {
             Abi::System => {}
         }
     }
+
+    fn check_builtin_attribute(&mut self, attr: &ast::Attribute, name: &str,
+                               template: AttributeTemplate) {
+        // Some special attributes like `cfg` must be checked
+        // before the generic check, so we skip them here.
+        let should_skip = |name| name == "cfg";
+        // Some of previously accepted forms were used in practice,
+        // report them as warnings for now.
+        let should_warn = |name| name == "doc" || name == "ignore" ||
+                                 name == "inline" || name == "link";
+
+        match attr.parse_meta(self.context.parse_sess) {
+            Ok(meta) => if !should_skip(name) && !template.compatible(&meta.node) {
+                let mut msg = "attribute must be of the form ".to_owned();
+                let mut first = true;
+                if template.word {
+                    first = false;
+                    msg.push_str(&format!("`#[{}{}]`", name, ""));
+                }
+                if let Some(descr) = template.list {
+                    if !first {
+                        msg.push_str(" or ");
+                    }
+                    first = false;
+                    msg.push_str(&format!("`#[{}({})]`", name, descr));
+                }
+                if let Some(descr) = template.name_value_str {
+                    if !first {
+                        msg.push_str(" or ");
+                    }
+                    msg.push_str(&format!("`#[{} = \"{}\"]`", name, descr));
+                }
+                if should_warn(name) {
+                    self.context.parse_sess.buffer_lint(
+                        BufferedEarlyLintId::IllFormedAttributeInput,
+                        meta.span,
+                        ast::CRATE_NODE_ID,
+                        &msg,
+                    );
+                } else {
+                    self.context.parse_sess.span_diagnostic.span_err(meta.span, &msg);
+                }
+            }
+            Err(mut err) => err.emit(),
+        }
+    }
 }
 
 impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
@@ -1517,12 +1632,15 @@ fn visit_attribute(&mut self, attr: &ast::Attribute) {
             }
         }
 
-        if !self.context.features.unrestricted_attribute_tokens {
-            // Unfortunately, `parse_meta` cannot be called speculatively
-            // because it can report errors by itself, so we have to call it
-            // only if the feature is disabled.
-            if let Err(mut err) = attr.parse_meta(self.context.parse_sess) {
-                err.help("try enabling `#![feature(unrestricted_attribute_tokens)]`").emit()
+        match BUILTIN_ATTRIBUTES.iter().find(|(name, ..)| attr.path == name) {
+            Some(&(name, _, template, _)) => self.check_builtin_attribute(attr, name, template),
+            None => if !self.context.features.unrestricted_attribute_tokens {
+                // Unfortunately, `parse_meta` cannot be called speculatively
+                // because it can report errors by itself, so we have to call it
+                // only if the feature is disabled.
+                if let Err(mut err) = attr.parse_meta(self.context.parse_sess) {
+                    err.help("try enabling `#![feature(unrestricted_attribute_tokens)]`").emit()
+                }
             }
         }
     }
@@ -1827,25 +1945,6 @@ fn visit_impl_item(&mut self, ii: &'a ast::ImplItem) {
         visit::walk_impl_item(self, ii);
     }
 
-    fn visit_path(&mut self, path: &'a ast::Path, _id: NodeId) {
-        for segment in &path.segments {
-            // Identifiers we are going to check could come from a legacy macro (e.g., `#[test]`).
-            // For such macros identifiers must have empty context, because this context is
-            // used during name resolution and produced names must be unhygienic for compatibility.
-            // On the other hand, we need the actual non-empty context for feature gate checking
-            // because it's hygienic even for legacy macros. As previously stated, such context
-            // cannot be kept in identifiers, so it's kept in paths instead and we take it from
-            // there while keeping location info from the ident span.
-            let span = segment.ident.span.with_ctxt(path.span.ctxt());
-            if segment.ident.name == keywords::Extern.name() {
-                gate_feature_post!(&self, extern_in_paths, span,
-                                   "`extern` in paths is experimental");
-            }
-        }
-
-        visit::walk_path(self, path);
-    }
-
     fn visit_vis(&mut self, vis: &'a ast::Visibility) {
         if let ast::VisibilityKind::Crate(ast::CrateSugar::JustCrate) = vis.node {
             gate_feature_post!(&self, crate_visibility_modifier, vis.span,
@@ -1945,11 +2044,7 @@ fn feature_removed(span_handler: &Handler, span: Span, reason: Option<&str>) {
 
         let list = match attr.meta_item_list() {
             Some(list) => list,
-            None => {
-                span_err!(span_handler, attr.span, E0555,
-                          "malformed feature attribute, expected #![feature(...)]");
-                continue
-            }
+            None => continue,
         };
 
         for mi in list {