From 63ee3fe566762781d1e60f190014b416f99fed36 Mon Sep 17 00:00:00 2001
From: Keegan McAllister <kmcallister@mozilla.com>
Date: Fri, 6 Mar 2015 12:56:28 -0800
Subject: [PATCH 1/3] Consolidate ExpansionConfig feature tests

---
 src/libsyntax/ext/expand.rs | 60 +++++++++++++------------------------
 1 file changed, 20 insertions(+), 40 deletions(-)

diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index d48108f17ff9a..84b0299925211 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -1447,6 +1447,19 @@ pub struct ExpansionConfig<'feat> {
     pub recursion_limit: usize,
 }
 
+macro_rules! feature_tests {
+    ($( fn $getter:ident = $field:ident, )*) => {
+        $(
+            pub fn $getter(&self) -> bool {
+                match self.features {
+                    Some(&Features { $field: true, .. }) => true,
+                    _ => false,
+                }
+            }
+        )*
+    }
+}
+
 impl<'feat> ExpansionConfig<'feat> {
     pub fn default(crate_name: String) -> ExpansionConfig<'static> {
         ExpansionConfig {
@@ -1456,46 +1469,13 @@ impl<'feat> ExpansionConfig<'feat> {
         }
     }
 
-    pub fn enable_quotes(&self) -> bool {
-        match self.features {
-            Some(&Features { allow_quote: true, .. }) => true,
-            _ => false,
-        }
-    }
-
-    pub fn enable_asm(&self) -> bool {
-        match self.features {
-            Some(&Features { allow_asm: true, .. }) => true,
-            _ => false,
-        }
-    }
-
-    pub fn enable_log_syntax(&self) -> bool {
-        match self.features {
-            Some(&Features { allow_log_syntax: true, .. }) => true,
-            _ => false,
-        }
-    }
-
-    pub fn enable_concat_idents(&self) -> bool {
-        match self.features {
-            Some(&Features { allow_concat_idents: true, .. }) => true,
-            _ => false,
-        }
-    }
-
-    pub fn enable_trace_macros(&self) -> bool {
-        match self.features {
-            Some(&Features { allow_trace_macros: true, .. }) => true,
-            _ => false,
-        }
-    }
-
-    pub fn enable_allow_internal_unstable(&self) -> bool {
-        match self.features {
-            Some(&Features { allow_internal_unstable: true, .. }) => true,
-            _ => false
-        }
+    feature_tests! {
+        fn enable_quotes = allow_quote,
+        fn enable_asm = allow_asm,
+        fn enable_log_syntax = allow_log_syntax,
+        fn enable_concat_idents = allow_concat_idents,
+        fn enable_trace_macros = allow_trace_macros,
+        fn enable_allow_internal_unstable = allow_internal_unstable,
     }
 }
 

From e60e6f0693adfd03340dff31023c7517dc3af1b5 Mon Sep 17 00:00:00 2001
From: Keegan McAllister <kmcallister@mozilla.com>
Date: Fri, 6 Mar 2015 15:10:20 -0800
Subject: [PATCH 2/3] Check gated attributes before and after macro expansion

This is important because attributes can affect expansion.
---
 src/libsyntax/feature_gate.rs                 | 75 ++++++++++---------
 src/test/compile-fail/deprecated-phase.rs     |  2 +
 ...ng-items.rs => feature-gate-intrinsics.rs} |  4 -
 .../compile-fail/feature-gate-lang-items.rs   | 15 ++++
 src/test/compile-fail/linkage1.rs             |  1 -
 src/test/compile-fail/malformed-plugin-1.rs   |  1 +
 src/test/compile-fail/malformed-plugin-2.rs   |  1 +
 src/test/compile-fail/malformed-plugin-3.rs   |  1 +
 .../plugin-extern-crate-attr-deprecated.rs    |  2 +
 .../compile-fail/reserved-attr-on-macro.rs    | 18 +++++
 src/test/run-pass-fulldeps/macro-crate.rs     |  2 +-
 11 files changed, 79 insertions(+), 43 deletions(-)
 rename src/test/compile-fail/{feature-gate-intrinsics-and-lang-items.rs => feature-gate-intrinsics.rs} (89%)
 create mode 100644 src/test/compile-fail/feature-gate-lang-items.rs
 create mode 100644 src/test/compile-fail/reserved-attr-on-macro.rs

diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index c6a94b26a0a51..eff86757fd0b2 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -196,6 +196,7 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType)] = &[
     ("no_mangle", Normal),
     ("no_link", Normal),
     ("derive", Normal),
+    ("deriving", Normal), // deprecation err in expansion
     ("should_fail", Normal),
     ("should_panic", Normal),
     ("ignore", Normal),
@@ -235,6 +236,9 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType)] = &[
                                    "the `#[rustc_move_fragments]` attribute \
                                     is an experimental feature")),
 
+    ("allow_internal_unstable", Gated("allow_internal_unstable",
+                                      EXPLAIN_ALLOW_INTERNAL_UNSTABLE)),
+
     // FIXME: #14408 whitelist docs since rustdoc looks at them
     ("doc", Whitelisted),
 
@@ -369,6 +373,33 @@ impl<'a> Context<'a> {
     fn has_feature(&self, feature: &str) -> bool {
         self.features.iter().any(|&n| n == feature)
     }
+
+    fn check_attribute(&self, attr: &ast::Attribute) {
+        debug!("check_attribute(attr = {:?})", attr);
+        let name = &*attr.name();
+        for &(n, ty) in KNOWN_ATTRIBUTES {
+            if n == name {
+                if let Gated(gate, desc) = ty {
+                    self.gate_feature(gate, attr.span, desc);
+                }
+                debug!("check_attribute: {:?} is known, {:?}", name, ty);
+                return;
+            }
+        }
+        if name.starts_with("rustc_") {
+            self.gate_feature("rustc_attrs", attr.span,
+                              "unless otherwise specified, attributes \
+                               with the prefix `rustc_` \
+                               are reserved for internal compiler diagnostics");
+        } else {
+            self.gate_feature("custom_attribute", attr.span,
+                       format!("The attribute `{}` is currently \
+                                unknown to the the compiler and \
+                                may have meaning \
+                                added to it in the future",
+                                name).as_slice());
+        }
+    }
 }
 
 pub fn emit_feature_err(diag: &SpanHandler, feature: &str, span: Span, explain: &str) {
@@ -436,10 +467,7 @@ impl<'a, 'v> Visitor<'v> for MacroVisitor<'a> {
     }
 
     fn visit_attribute(&mut self, attr: &'v ast::Attribute) {
-        if attr.name() == "allow_internal_unstable" {
-            self.context.gate_feature("allow_internal_unstable", attr.span,
-                                      EXPLAIN_ALLOW_INTERNAL_UNSTABLE)
-        }
+        self.context.check_attribute(attr);
     }
 }
 
@@ -456,6 +484,12 @@ impl<'a> PostExpansionVisitor<'a> {
 }
 
 impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
+    fn visit_attribute(&mut self, attr: &ast::Attribute) {
+        if !self.context.cm.span_allows_unstable(attr.span) {
+            self.context.check_attribute(attr);
+        }
+    }
+
     fn visit_name(&mut self, sp: Span, name: ast::Name) {
         if !token::get_name(name).is_ascii() {
             self.gate_feature("non_ascii_idents", sp,
@@ -556,12 +590,6 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
     }
 
     fn visit_foreign_item(&mut self, i: &ast::ForeignItem) {
-        if attr::contains_name(&i.attrs, "linkage") {
-            self.gate_feature("linkage", i.span,
-                              "the `linkage` attribute is experimental \
-                               and not portable across platforms")
-        }
-
         let links_to_llvm = match attr::first_attr_value_str_by_name(&i.attrs,
                                                                      "link_name") {
             Some(val) => val.starts_with("llvm."),
@@ -636,33 +664,6 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
         visit::walk_expr(self, e);
     }
 
-    fn visit_attribute(&mut self, attr: &ast::Attribute) {
-        debug!("visit_attribute(attr = {:?})", attr);
-        let name = &*attr.name();
-        for &(n, ty) in KNOWN_ATTRIBUTES {
-            if n == name {
-                if let Gated(gate, desc) = ty {
-                    self.gate_feature(gate, attr.span, desc);
-                }
-                debug!("visit_attribute: {:?} is known, {:?}", name, ty);
-                return;
-            }
-        }
-        if name.starts_with("rustc_") {
-            self.gate_feature("rustc_attrs", attr.span,
-                              "unless otherwise specified, attributes \
-                               with the prefix `rustc_` \
-                               are reserved for internal compiler diagnostics");
-        } else {
-            self.gate_feature("custom_attribute", attr.span,
-                       format!("The attribute `{}` is currently \
-                                unknown to the the compiler and \
-                                may have meaning \
-                                added to it in the future",
-                                name).as_slice());
-        }
-    }
-
     fn visit_pat(&mut self, pattern: &ast::Pat) {
         match pattern.node {
             ast::PatVec(_, Some(_), ref last) if !last.is_empty() => {
diff --git a/src/test/compile-fail/deprecated-phase.rs b/src/test/compile-fail/deprecated-phase.rs
index 1401494d987a2..22fc4a94cd25a 100644
--- a/src/test/compile-fail/deprecated-phase.rs
+++ b/src/test/compile-fail/deprecated-phase.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![feature(custom_attribute)]
+
 #[phase(blah)]
 //~^ ERROR #[phase] is deprecated
 extern crate foo;
diff --git a/src/test/compile-fail/feature-gate-intrinsics-and-lang-items.rs b/src/test/compile-fail/feature-gate-intrinsics.rs
similarity index 89%
rename from src/test/compile-fail/feature-gate-intrinsics-and-lang-items.rs
rename to src/test/compile-fail/feature-gate-intrinsics.rs
index 986d52b1787ec..a4c09b21c90a6 100644
--- a/src/test/compile-fail/feature-gate-intrinsics-and-lang-items.rs
+++ b/src/test/compile-fail/feature-gate-intrinsics.rs
@@ -8,9 +8,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#[lang="foo"]   //~ ERROR language items are subject to change
-trait Foo {}
-
 extern "rust-intrinsic" {   //~ ERROR intrinsics are subject to change
     fn bar();
 }
@@ -20,4 +17,3 @@ extern "rust-intrinsic" fn baz() {  //~ ERROR intrinsics are subject to change
 
 fn main() {
 }
-
diff --git a/src/test/compile-fail/feature-gate-lang-items.rs b/src/test/compile-fail/feature-gate-lang-items.rs
new file mode 100644
index 0000000000000..0435ff4c332d5
--- /dev/null
+++ b/src/test/compile-fail/feature-gate-lang-items.rs
@@ -0,0 +1,15 @@
+// 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 <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.
+
+#[lang="foo"]   //~ ERROR language items are subject to change
+trait Foo {}
+
+fn main() {
+}
diff --git a/src/test/compile-fail/linkage1.rs b/src/test/compile-fail/linkage1.rs
index 35f93c13fb5e2..555cc2b9a7aad 100644
--- a/src/test/compile-fail/linkage1.rs
+++ b/src/test/compile-fail/linkage1.rs
@@ -11,5 +11,4 @@
 extern {
     #[linkage = "extern_weak"] static foo: isize;
     //~^ ERROR: the `linkage` attribute is experimental and not portable
-    //~^^ ERROR: the `linkage` attribute is experimental and not portable
 }
diff --git a/src/test/compile-fail/malformed-plugin-1.rs b/src/test/compile-fail/malformed-plugin-1.rs
index 254a797ef1cd0..214a5e5e3eb11 100644
--- a/src/test/compile-fail/malformed-plugin-1.rs
+++ b/src/test/compile-fail/malformed-plugin-1.rs
@@ -8,6 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![feature(plugin)]
 #![plugin] //~ ERROR malformed plugin attribute
 
 fn main() {}
diff --git a/src/test/compile-fail/malformed-plugin-2.rs b/src/test/compile-fail/malformed-plugin-2.rs
index 884087b7bc534..1b112608beeb2 100644
--- a/src/test/compile-fail/malformed-plugin-2.rs
+++ b/src/test/compile-fail/malformed-plugin-2.rs
@@ -8,6 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![feature(plugin)]
 #![plugin="bleh"] //~ ERROR malformed plugin attribute
 
 fn main() {}
diff --git a/src/test/compile-fail/malformed-plugin-3.rs b/src/test/compile-fail/malformed-plugin-3.rs
index 4885bb901df68..0c948831de217 100644
--- a/src/test/compile-fail/malformed-plugin-3.rs
+++ b/src/test/compile-fail/malformed-plugin-3.rs
@@ -8,6 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![feature(plugin)]
 #![plugin(foo="bleh")] //~ ERROR malformed plugin attribute
 
 fn main() {}
diff --git a/src/test/compile-fail/plugin-extern-crate-attr-deprecated.rs b/src/test/compile-fail/plugin-extern-crate-attr-deprecated.rs
index ccda5cbdceba6..efa352e386d4d 100644
--- a/src/test/compile-fail/plugin-extern-crate-attr-deprecated.rs
+++ b/src/test/compile-fail/plugin-extern-crate-attr-deprecated.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![feature(plugin)]
+
 #[plugin]  //~ ERROR #[plugin] on `extern crate` is deprecated
 //~^ HELP use a crate attribute instead, i.e. #![plugin(std)]
 extern crate std;
diff --git a/src/test/compile-fail/reserved-attr-on-macro.rs b/src/test/compile-fail/reserved-attr-on-macro.rs
new file mode 100644
index 0000000000000..db8f82a70e103
--- /dev/null
+++ b/src/test/compile-fail/reserved-attr-on-macro.rs
@@ -0,0 +1,18 @@
+// Copyright 2015 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.
+
+#[rustc_attribute_should_be_reserved] //~ ERROR attributes with the prefix `rustc_` are reserved
+macro_rules! foo {
+    () => (());
+}
+
+fn main() {
+    foo!();
+}
diff --git a/src/test/run-pass-fulldeps/macro-crate.rs b/src/test/run-pass-fulldeps/macro-crate.rs
index 58ccd79b7121d..7a2846c31b663 100644
--- a/src/test/run-pass-fulldeps/macro-crate.rs
+++ b/src/test/run-pass-fulldeps/macro-crate.rs
@@ -11,7 +11,7 @@
 // aux-build:macro_crate_test.rs
 // ignore-stage1
 
-#![feature(plugin)]
+#![feature(plugin, custom_attribute)]
 #![plugin(macro_crate_test)]
 
 #[macro_use] #[no_link]

From 491054f08e2aaaa8438f1a9943f115dad9da1c6b Mon Sep 17 00:00:00 2001
From: Keegan McAllister <kmcallister@mozilla.com>
Date: Fri, 6 Mar 2015 13:15:54 -0800
Subject: [PATCH 3/3] Make #[derive(Anything)] into sugar for
 #[derive_Anything]

This is a hack, but I don't think we can do much better as long as `derive` is
running at the syntax expansion phase.

If the custom_derive feature gate is enabled, this works with user-defined
traits and syntax extensions. Without the gate, you can't use e.g. #[derive_Clone]
directly, so this does not change the stable language.

This commit also cleans up the deriving code somewhat, and forbids some
previously-meaningless attribute syntax. For this reason it's technically a

    [breaking-change]
---
 src/doc/reference.md                          |   4 +
 src/libsyntax/ext/base.rs                     |   6 +-
 src/libsyntax/ext/deriving/bounds.rs          |  45 ++--
 src/libsyntax/ext/deriving/mod.rs             | 212 ++++++++++--------
 src/libsyntax/ext/expand.rs                   |   1 +
 src/libsyntax/feature_gate.rs                 |  14 ++
 src/test/auxiliary/custom_derive_plugin.rs    |  74 ++++++
 src/test/compile-fail/deriving-bounds.rs      |   6 +-
 .../deriving-meta-unknown-trait.rs            |   3 +-
 .../compile-fail/malformed-derive-entry.rs    |  25 +++
 src/test/compile-fail/single-derive-attr.rs   |  15 ++
 src/test/run-pass-fulldeps/derive-totalsum.rs |  59 +++++
 src/test/run-pass/deprecated-derive.rs        |  15 ++
 .../run-pass/single-derive-attr-with-gate.rs  |  18 ++
 14 files changed, 372 insertions(+), 125 deletions(-)
 create mode 100644 src/test/auxiliary/custom_derive_plugin.rs
 create mode 100644 src/test/compile-fail/malformed-derive-entry.rs
 create mode 100644 src/test/compile-fail/single-derive-attr.rs
 create mode 100644 src/test/run-pass-fulldeps/derive-totalsum.rs
 create mode 100644 src/test/run-pass/deprecated-derive.rs
 create mode 100644 src/test/run-pass/single-derive-attr-with-gate.rs

diff --git a/src/doc/reference.md b/src/doc/reference.md
index 95ccc71532a34..a772d98583e49 100644
--- a/src/doc/reference.md
+++ b/src/doc/reference.md
@@ -2429,6 +2429,10 @@ The currently implemented features of the reference compiler are:
                        so that new attributes can be added in a bacwards compatible
                        manner (RFC 572).
 
+* `custom_derive` - Allows the use of `#[derive(Foo,Bar)]` as sugar for
+                    `#[derive_Foo] #[derive_Bar]`, which can be user-defined syntax
+                    extensions.
+
 * `intrinsics` - Allows use of the "rust-intrinsics" ABI. Compiler intrinsics
                  are inherently unstable and no promise about them is made.
 
diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs
index 5513b44235d2c..8aeafe419daa9 100644
--- a/src/libsyntax/ext/base.rs
+++ b/src/libsyntax/ext/base.rs
@@ -491,10 +491,8 @@ fn initial_syntax_expander_table<'feat>(ecfg: &expand::ExpansionConfig<'feat>)
     syntax_expanders.insert(intern("log_syntax"),
                             builtin_normal_expander(
                                     ext::log_syntax::expand_syntax_ext));
-    syntax_expanders.insert(intern("derive"),
-                            Decorator(Box::new(ext::deriving::expand_meta_derive)));
-    syntax_expanders.insert(intern("deriving"),
-                            Decorator(Box::new(ext::deriving::expand_deprecated_deriving)));
+
+    ext::deriving::register_all(&mut syntax_expanders);
 
     if ecfg.enable_quotes() {
         // Quasi-quoting expanders
diff --git a/src/libsyntax/ext/deriving/bounds.rs b/src/libsyntax/ext/deriving/bounds.rs
index 93098484ae013..e408c99935d1c 100644
--- a/src/libsyntax/ext/deriving/bounds.rs
+++ b/src/libsyntax/ext/deriving/bounds.rs
@@ -8,47 +8,34 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use ast::{MetaItem, MetaWord, Item};
+use ast::{MetaItem, Item};
 use codemap::Span;
 use ext::base::ExtCtxt;
 use ext::deriving::generic::*;
 use ext::deriving::generic::ty::*;
 use ptr::P;
 
-pub fn expand_deriving_bound<F>(cx: &mut ExtCtxt,
-                                span: Span,
-                                mitem: &MetaItem,
-                                item: &Item,
-                                push: F) where
+pub fn expand_deriving_unsafe_bound<F>(cx: &mut ExtCtxt,
+                                       span: Span,
+                                       _: &MetaItem,
+                                       _: &Item,
+                                       _: F) where
     F: FnOnce(P<Item>),
 {
-    let name = match mitem.node {
-        MetaWord(ref tname) => {
-            match &tname[..] {
-                "Copy" => "Copy",
-                "Send" | "Sync" => {
-                    return cx.span_err(span,
-                                       &format!("{} is an unsafe trait and it \
-                                                 should be implemented explicitly",
-                                                *tname))
-                }
-                ref tname => {
-                    cx.span_bug(span,
-                                &format!("expected built-in trait name but \
-                                          found {}", *tname))
-                }
-            }
-        },
-        _ => {
-            return cx.span_err(span, "unexpected value in deriving, expected \
-                                      a trait")
-        }
-    };
+    cx.span_err(span, "this unsafe trait should be implemented explicitly");
+}
 
+pub fn expand_deriving_copy<F>(cx: &mut ExtCtxt,
+                               span: Span,
+                               mitem: &MetaItem,
+                               item: &Item,
+                               push: F) where
+    F: FnOnce(P<Item>),
+{
     let path = Path::new(vec![
         if cx.use_std { "std" } else { "core" },
         "marker",
-        name
+        "Copy",
     ]);
 
     let trait_def = TraitDef {
diff --git a/src/libsyntax/ext/deriving/mod.rs b/src/libsyntax/ext/deriving/mod.rs
index 973c8f5fa1e0b..2631c28cf2fe9 100644
--- a/src/libsyntax/ext/deriving/mod.rs
+++ b/src/libsyntax/ext/deriving/mod.rs
@@ -1,4 +1,4 @@
-// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
+// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
 // file at the top-level directory of this distribution and at
 // http://rust-lang.org/COPYRIGHT.
 //
@@ -13,9 +13,13 @@
 //! FIXME (#2810): hygiene. Search for "__" strings (in other files too). We also assume "extra" is
 //! the standard library, and "std" is the core library.
 
-use ast::{Item, MetaItem, MetaList, MetaNameValue, MetaWord};
-use ext::base::ExtCtxt;
+use ast::{Item, MetaItem, MetaWord};
+use attr::AttrMetaMethods;
+use ext::base::{ExtCtxt, SyntaxEnv, Decorator, ItemDecorator, Modifier};
+use ext::build::AstBuilder;
+use feature_gate;
 use codemap::Span;
+use parse::token::{intern, intern_and_get_ident};
 use ptr::P;
 
 macro_rules! pathvec {
@@ -74,101 +78,133 @@ pub mod totalord;
 
 pub mod generic;
 
-pub fn expand_deprecated_deriving(cx: &mut ExtCtxt,
-                                  span: Span,
-                                  _: &MetaItem,
-                                  _: &Item,
-                                  _: &mut FnMut(P<Item>)) {
+fn expand_deprecated_deriving(cx: &mut ExtCtxt,
+                              span: Span,
+                              _: &MetaItem,
+                              _: &Item,
+                              _: &mut FnMut(P<Item>)) {
     cx.span_err(span, "`deriving` has been renamed to `derive`");
 }
 
-pub fn expand_meta_derive(cx: &mut ExtCtxt,
-                          _span: Span,
-                          mitem: &MetaItem,
-                          item: &Item,
-                          push: &mut FnMut(P<Item>)) {
-    match mitem.node {
-        MetaNameValue(_, ref l) => {
-            cx.span_err(l.span, "unexpected value in `derive`");
+fn expand_derive(cx: &mut ExtCtxt,
+                 _: Span,
+                 mitem: &MetaItem,
+                 item: P<Item>) -> P<Item> {
+    item.map(|mut item| {
+        if mitem.value_str().is_some() {
+            cx.span_err(mitem.span, "unexpected value in `derive`");
         }
-        MetaWord(_) => {
+
+        let traits = mitem.meta_item_list().unwrap_or(&[]);
+        if traits.is_empty() {
             cx.span_warn(mitem.span, "empty trait list in `derive`");
         }
-        MetaList(_, ref titems) if titems.len() == 0 => {
-            cx.span_warn(mitem.span, "empty trait list in `derive`");
+
+        for titem in traits.iter().rev() {
+            let tname = match titem.node {
+                MetaWord(ref tname) => tname,
+                _ => {
+                    cx.span_err(titem.span, "malformed `derive` entry");
+                    continue;
+                }
+            };
+
+            if !(is_builtin_trait(tname) || cx.ecfg.enable_custom_derive()) {
+                feature_gate::emit_feature_err(&cx.parse_sess.span_diagnostic,
+                                               "custom_derive",
+                                               titem.span,
+                                               feature_gate::EXPLAIN_CUSTOM_DERIVE);
+                continue;
+            }
+
+            // #[derive(Foo, Bar)] expands to #[derive_Foo] #[derive_Bar]
+            item.attrs.push(cx.attribute(titem.span, cx.meta_word(titem.span,
+                intern_and_get_ident(&format!("derive_{}", tname)))));
         }
-        MetaList(_, ref titems) => {
-            for titem in titems.iter().rev() {
-                match titem.node {
-                    MetaNameValue(ref tname, _) |
-                    MetaList(ref tname, _) |
-                    MetaWord(ref tname) => {
-                        macro_rules! expand {
-                            ($func:path) => ($func(cx, titem.span, &**titem, item,
-                                                   |i| push(i)))
-                        }
-
-                        match &tname[..] {
-                            "Clone" => expand!(clone::expand_deriving_clone),
-
-                            "Hash" => expand!(hash::expand_deriving_hash),
-
-                            "RustcEncodable" => {
-                                expand!(encodable::expand_deriving_rustc_encodable)
-                            }
-                            "RustcDecodable" => {
-                                expand!(decodable::expand_deriving_rustc_decodable)
-                            }
-                            "Encodable" => {
-                                cx.span_warn(titem.span,
-                                             "derive(Encodable) is deprecated \
-                                              in favor of derive(RustcEncodable)");
-
-                                expand!(encodable::expand_deriving_encodable)
-                            }
-                            "Decodable" => {
-                                cx.span_warn(titem.span,
-                                             "derive(Decodable) is deprecated \
-                                              in favor of derive(RustcDecodable)");
-
-                                expand!(decodable::expand_deriving_decodable)
-                            }
-
-                            "PartialEq" => expand!(eq::expand_deriving_eq),
-                            "Eq" => expand!(totaleq::expand_deriving_totaleq),
-                            "PartialOrd" => expand!(ord::expand_deriving_ord),
-                            "Ord" => expand!(totalord::expand_deriving_totalord),
-
-                            "Rand" => expand!(rand::expand_deriving_rand),
-
-                            "Show" => {
-                                cx.span_warn(titem.span,
-                                             "derive(Show) is deprecated \
-                                              in favor of derive(Debug)");
-
-                                expand!(show::expand_deriving_show)
-                            },
-
-                            "Debug" => expand!(show::expand_deriving_show),
-
-                            "Default" => expand!(default::expand_deriving_default),
-
-                            "FromPrimitive" => expand!(primitive::expand_deriving_from_primitive),
-
-                            "Send" => expand!(bounds::expand_deriving_bound),
-                            "Sync" => expand!(bounds::expand_deriving_bound),
-                            "Copy" => expand!(bounds::expand_deriving_bound),
-
-                            ref tname => {
-                                cx.span_err(titem.span,
-                                            &format!("unknown `derive` \
-                                                     trait: `{}`",
-                                                    *tname));
-                            }
-                        };
+
+        item
+    })
+}
+
+macro_rules! derive_traits {
+    ($( $name:expr => $func:path, )*) => {
+        pub fn register_all(env: &mut SyntaxEnv) {
+            // Define the #[derive_*] extensions.
+            $({
+                struct DeriveExtension;
+
+                impl ItemDecorator for DeriveExtension {
+                    fn expand(&self,
+                              ecx: &mut ExtCtxt,
+                              sp: Span,
+                              mitem: &MetaItem,
+                              item: &Item,
+                              push: &mut FnMut(P<Item>)) {
+                        warn_if_deprecated(ecx, sp, $name);
+                        $func(ecx, sp, mitem, item, |i| push(i));
                     }
                 }
+
+                env.insert(intern(concat!("derive_", $name)),
+                           Decorator(Box::new(DeriveExtension)));
+            })*
+
+            env.insert(intern("derive"),
+                       Modifier(Box::new(expand_derive)));
+            env.insert(intern("deriving"),
+                       Decorator(Box::new(expand_deprecated_deriving)));
+        }
+
+        fn is_builtin_trait(name: &str) -> bool {
+            match name {
+                $( $name )|* => true,
+                _ => false,
             }
         }
     }
 }
+
+derive_traits! {
+    "Clone" => clone::expand_deriving_clone,
+
+    "Hash" => hash::expand_deriving_hash,
+
+    "RustcEncodable" => encodable::expand_deriving_rustc_encodable,
+
+    "RustcDecodable" => decodable::expand_deriving_rustc_decodable,
+
+    "PartialEq" => eq::expand_deriving_eq,
+    "Eq" => totaleq::expand_deriving_totaleq,
+    "PartialOrd" => ord::expand_deriving_ord,
+    "Ord" => totalord::expand_deriving_totalord,
+
+    "Rand" => rand::expand_deriving_rand,
+
+    "Debug" => show::expand_deriving_show,
+
+    "Default" => default::expand_deriving_default,
+
+    "FromPrimitive" => primitive::expand_deriving_from_primitive,
+
+    "Send" => bounds::expand_deriving_unsafe_bound,
+    "Sync" => bounds::expand_deriving_unsafe_bound,
+    "Copy" => bounds::expand_deriving_copy,
+
+    // deprecated
+    "Show" => show::expand_deriving_show,
+    "Encodable" => encodable::expand_deriving_encodable,
+    "Decodable" => decodable::expand_deriving_decodable,
+}
+
+#[inline] // because `name` is a compile-time constant
+fn warn_if_deprecated(ecx: &mut ExtCtxt, sp: Span, name: &str) {
+    if let Some(replacement) = match name {
+        "Show" => Some("Debug"),
+        "Encodable" => Some("RustcEncodable"),
+        "Decodable" => Some("RustcDecodable"),
+        _ => None,
+    } {
+        ecx.span_warn(sp, &format!("derive({}) is deprecated in favor of derive({})",
+                                   name, replacement));
+    }
+}
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index 84b0299925211..db8819ef82c2e 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -1476,6 +1476,7 @@ impl<'feat> ExpansionConfig<'feat> {
         fn enable_concat_idents = allow_concat_idents,
         fn enable_trace_macros = allow_trace_macros,
         fn enable_allow_internal_unstable = allow_internal_unstable,
+        fn enable_custom_derive = allow_custom_derive,
     }
 }
 
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index eff86757fd0b2..425c517cb29fd 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -137,6 +137,10 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Status)] = &[
     // Allows the use of custom attributes; RFC 572
     ("custom_attribute", "1.0.0", Active),
 
+    // Allows the use of #[derive(Anything)] as sugar for
+    // #[derive_Anything].
+    ("custom_derive", "1.0.0", Active),
+
     // Allows the use of rustc_* attributes; RFC 572
     ("rustc_attrs", "1.0.0", Active),
 
@@ -319,6 +323,7 @@ pub struct Features {
     pub allow_concat_idents: bool,
     pub allow_trace_macros: bool,
     pub allow_internal_unstable: bool,
+    pub allow_custom_derive: bool,
     pub old_orphan_check: bool,
     pub simd_ffi: bool,
     pub unmarked_api: bool,
@@ -340,6 +345,7 @@ impl Features {
             allow_concat_idents: false,
             allow_trace_macros: false,
             allow_internal_unstable: false,
+            allow_custom_derive: false,
             old_orphan_check: false,
             simd_ffi: false,
             unmarked_api: false,
@@ -391,6 +397,10 @@ impl<'a> Context<'a> {
                               "unless otherwise specified, attributes \
                                with the prefix `rustc_` \
                                are reserved for internal compiler diagnostics");
+        } else if name.starts_with("derive_") {
+            self.gate_feature("custom_derive", attr.span,
+                              "attributes of the form `#[derive_*]` are reserved
+                               for the compiler");
         } else {
             self.gate_feature("custom_attribute", attr.span,
                        format!("The attribute `{}` is currently \
@@ -432,6 +442,9 @@ pub const EXPLAIN_TRACE_MACROS: &'static str =
 pub const EXPLAIN_ALLOW_INTERNAL_UNSTABLE: &'static str =
     "allow_internal_unstable side-steps feature gating and stability checks";
 
+pub const EXPLAIN_CUSTOM_DERIVE: &'static str =
+    "`#[derive]` for custom traits is not stable enough for use and is subject to change";
+
 struct MacroVisitor<'a> {
     context: &'a Context<'a>
 }
@@ -780,6 +793,7 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::C
         allow_concat_idents: cx.has_feature("concat_idents"),
         allow_trace_macros: cx.has_feature("trace_macros"),
         allow_internal_unstable: cx.has_feature("allow_internal_unstable"),
+        allow_custom_derive: cx.has_feature("custom_derive"),
         old_orphan_check: cx.has_feature("old_orphan_check"),
         simd_ffi: cx.has_feature("simd_ffi"),
         unmarked_api: cx.has_feature("unmarked_api"),
diff --git a/src/test/auxiliary/custom_derive_plugin.rs b/src/test/auxiliary/custom_derive_plugin.rs
new file mode 100644
index 0000000000000..e268896480480
--- /dev/null
+++ b/src/test/auxiliary/custom_derive_plugin.rs
@@ -0,0 +1,74 @@
+// Copyright 2015 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.
+
+// force-host
+
+#![feature(plugin_registrar)]
+#![feature(box_syntax)]
+#![feature(rustc_private)]
+
+extern crate syntax;
+extern crate rustc;
+
+use syntax::ast;
+use syntax::codemap::Span;
+use syntax::ext::base::{Decorator, ExtCtxt};
+use syntax::ext::build::AstBuilder;
+use syntax::ext::deriving::generic::{cs_fold, TraitDef, MethodDef, combine_substructure};
+use syntax::ext::deriving::generic::ty::{Literal, LifetimeBounds, Path, borrowed_explicit_self};
+use syntax::parse::token;
+use syntax::ptr::P;
+use rustc::plugin::Registry;
+
+#[plugin_registrar]
+pub fn plugin_registrar(reg: &mut Registry) {
+    reg.register_syntax_extension(
+        token::intern("derive_TotalSum"),
+        Decorator(box expand));
+}
+
+fn expand(cx: &mut ExtCtxt,
+          span: Span,
+          mitem: &ast::MetaItem,
+          item: &ast::Item,
+          push: &mut FnMut(P<ast::Item>)) {
+    let trait_def = TraitDef {
+        span: span,
+        attributes: vec![],
+        path: Path::new(vec!["TotalSum"]),
+        additional_bounds: vec![],
+        generics: LifetimeBounds::empty(),
+        associated_types: vec![],
+        methods: vec![
+            MethodDef {
+                name: "total_sum",
+                generics: LifetimeBounds::empty(),
+                explicit_self: borrowed_explicit_self(),
+                args: vec![],
+                ret_ty: Literal(Path::new_local("isize")),
+                attributes: vec![],
+                combine_substructure: combine_substructure(box |cx, span, substr| {
+                    let zero = cx.expr_int(span, 0);
+                    cs_fold(false,
+                            |cx, span, subexpr, field, _| {
+                                cx.expr_binary(span, ast::BiAdd, subexpr,
+                                    cx.expr_method_call(span, field,
+                                        token::str_to_ident("total_sum"), vec![]))
+                            },
+                            zero,
+                            box |cx, span, _, _| { cx.span_bug(span, "wtf??"); },
+                            cx, span, substr)
+                }),
+            },
+        ],
+    };
+
+    trait_def.expand(cx, mitem, item, |i| push(i))
+}
diff --git a/src/test/compile-fail/deriving-bounds.rs b/src/test/compile-fail/deriving-bounds.rs
index c0bcbb284a15e..72d06274de471 100644
--- a/src/test/compile-fail/deriving-bounds.rs
+++ b/src/test/compile-fail/deriving-bounds.rs
@@ -8,12 +8,12 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#[derive(Copy(Bad))]
-//~^ ERROR unexpected value in deriving, expected a trait
+#[derive(Send)]
+//~^ ERROR this unsafe trait should be implemented explicitly
 struct Test;
 
 #[derive(Sync)]
-//~^ ERROR Sync is an unsafe trait and it should be implemented explicitly
+//~^ ERROR this unsafe trait should be implemented explicitly
 struct Test1;
 
 pub fn main() {}
diff --git a/src/test/compile-fail/deriving-meta-unknown-trait.rs b/src/test/compile-fail/deriving-meta-unknown-trait.rs
index 6b85656bdd9e6..e223499469355 100644
--- a/src/test/compile-fail/deriving-meta-unknown-trait.rs
+++ b/src/test/compile-fail/deriving-meta-unknown-trait.rs
@@ -8,7 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#[derive(Eqr)] //~ ERROR unknown `derive` trait: `Eqr`
+#[derive(Eqr)]
+//~^ ERROR `#[derive]` for custom traits is not stable enough for use and is subject to change
 struct Foo;
 
 pub fn main() {}
diff --git a/src/test/compile-fail/malformed-derive-entry.rs b/src/test/compile-fail/malformed-derive-entry.rs
new file mode 100644
index 0000000000000..62dbc21495a54
--- /dev/null
+++ b/src/test/compile-fail/malformed-derive-entry.rs
@@ -0,0 +1,25 @@
+// Copyright 2015 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.
+
+#[derive(Copy(Bad))]
+//~^ ERROR malformed `derive` entry
+struct Test1;
+
+#[derive(Copy="bad")]
+//~^ ERROR malformed `derive` entry
+struct Test2;
+
+#[derive()]
+//~^ WARNING empty trait list
+struct Test3;
+
+#[derive]
+//~^ WARNING empty trait list
+struct Test4;
diff --git a/src/test/compile-fail/single-derive-attr.rs b/src/test/compile-fail/single-derive-attr.rs
new file mode 100644
index 0000000000000..0b1b3141f5bf7
--- /dev/null
+++ b/src/test/compile-fail/single-derive-attr.rs
@@ -0,0 +1,15 @@
+// Copyright 2015 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.
+
+#[derive_Clone]
+//~^ ERROR attributes of the form `#[derive_*]` are reserved
+struct Test;
+
+pub fn main() {}
diff --git a/src/test/run-pass-fulldeps/derive-totalsum.rs b/src/test/run-pass-fulldeps/derive-totalsum.rs
new file mode 100644
index 0000000000000..848b2425e4496
--- /dev/null
+++ b/src/test/run-pass-fulldeps/derive-totalsum.rs
@@ -0,0 +1,59 @@
+// Copyright 2015 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.
+
+// aux-build:custom_derive_plugin.rs
+// ignore-stage1
+
+#![feature(plugin, custom_derive)]
+#![plugin(custom_derive_plugin)]
+
+trait TotalSum {
+    fn total_sum(&self) -> isize;
+}
+
+impl TotalSum for isize {
+    fn total_sum(&self) -> isize {
+        *self
+    }
+}
+
+struct Seven;
+
+impl TotalSum for Seven {
+    fn total_sum(&self) -> isize {
+        7
+    }
+}
+
+#[derive(TotalSum)]
+struct Foo {
+    seven: Seven,
+    bar: Bar,
+    baz: isize,
+}
+
+#[derive(TotalSum)]
+struct Bar {
+    quux: isize,
+    bleh: isize,
+}
+
+
+pub fn main() {
+    let v = Foo {
+        seven: Seven,
+        bar: Bar {
+            quux: 9,
+            bleh: 3,
+        },
+        baz: 80,
+    };
+    assert_eq!(v.total_sum(), 99);
+}
diff --git a/src/test/run-pass/deprecated-derive.rs b/src/test/run-pass/deprecated-derive.rs
new file mode 100644
index 0000000000000..494d62c773770
--- /dev/null
+++ b/src/test/run-pass/deprecated-derive.rs
@@ -0,0 +1,15 @@
+// Copyright 2015 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.
+
+#[derive(Show)]
+//~^ WARNING derive(Show) is deprecated
+struct Test1;
+
+fn main() { }
diff --git a/src/test/run-pass/single-derive-attr-with-gate.rs b/src/test/run-pass/single-derive-attr-with-gate.rs
new file mode 100644
index 0000000000000..cc5d8fc78911d
--- /dev/null
+++ b/src/test/run-pass/single-derive-attr-with-gate.rs
@@ -0,0 +1,18 @@
+// Copyright 2015 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.
+
+#![feature(custom_derive)]
+
+#[derive_Clone]
+struct Test;
+
+pub fn main() {
+    Test.clone();
+}