about summary refs log tree commit diff
diff options
context:
space:
mode:
authorCorey Farwell <coreyf@rwell.org>2017-02-05 09:14:46 -0500
committerGitHub <noreply@github.com>2017-02-05 09:14:46 -0500
commit805a99e6cb37ab9d6a873f8e1fdf57f08b508e73 (patch)
treebc203d6f512f9375e3e8783378b77afee206e8db
parent65b24779a95d306ac9abd73bf430380e925e765e (diff)
parenta201348775081ae24bbd016b089b93906f44e3b7 (diff)
downloadrust-805a99e6cb37ab9d6a873f8e1fdf57f08b508e73.tar.gz
rust-805a99e6cb37ab9d6a873f8e1fdf57f08b508e73.zip
Rollup merge of #39442 - keeperofdakeys:expand-derives, r=jseyfried
Expand derive macros in the MacroExpander

This removes the expand_derives function, and sprinkles the functionality throughout the Invocation Collector, Expander and Resolver.

Fixes https://github.com/rust-lang/rust/issues/39326

r? @jseyfried
-rw-r--r--src/librustc_metadata/creader.rs6
-rw-r--r--src/librustc_resolve/build_reduced_graph.rs2
-rw-r--r--src/librustc_resolve/macros.rs26
-rw-r--r--src/libsyntax/ext/base.rs24
-rw-r--r--src/libsyntax/ext/derive.rs218
-rw-r--r--src/libsyntax/ext/expand.rs114
-rw-r--r--src/libsyntax/lib.rs1
-rw-r--r--src/libsyntax_ext/deriving/custom.rs18
-rw-r--r--src/libsyntax_ext/deriving/decodable.rs2
-rw-r--r--src/libsyntax_ext/deriving/encodable.rs2
-rw-r--r--src/libsyntax_ext/deriving/mod.rs257
-rw-r--r--src/libsyntax_ext/lib.rs7
-rw-r--r--src/libsyntax_ext/proc_macro_registrar.rs8
-rw-r--r--src/test/compile-fail-fulldeps/proc-macro/derive-bad.rs2
-rw-r--r--src/test/compile-fail-fulldeps/proc-macro/load-panic.rs2
-rw-r--r--src/test/compile-fail-fulldeps/proc-macro/no-macro-use-attr.rs2
-rw-r--r--src/test/compile-fail/deriving-meta-unknown-trait.rs2
-rw-r--r--src/test/compile-fail/deriving-primitive.rs2
-rw-r--r--src/test/compile-fail/macro-error.rs2
-rw-r--r--src/test/compile-fail/macros-nonfatal-errors.rs8
-rw-r--r--src/test/compile-fail/no-link.rs2
-rw-r--r--src/test/pretty/attr-derive.rs43
-rw-r--r--src/test/pretty/attr-variant-data.rs51
-rw-r--r--src/test/pretty/auxiliary/derive-foo.rs22
-rw-r--r--src/test/ui/custom-derive/issue-36935.stderr2
25 files changed, 483 insertions, 342 deletions
diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs
index f0d056b96f1..81a4f7c93b6 100644
--- a/src/librustc_metadata/creader.rs
+++ b/src/librustc_metadata/creader.rs
@@ -584,7 +584,7 @@ impl<'a> CrateLoader<'a> {
         use proc_macro::TokenStream;
         use proc_macro::__internal::Registry;
         use rustc_back::dynamic_lib::DynamicLibrary;
-        use syntax_ext::deriving::custom::CustomDerive;
+        use syntax_ext::deriving::custom::ProcMacroDerive;
         use syntax_ext::proc_macro_impl::AttrProcMacro;
 
         let path = match dylib {
@@ -616,8 +616,8 @@ impl<'a> CrateLoader<'a> {
                                       expand: fn(TokenStream) -> TokenStream,
                                       attributes: &[&'static str]) {
                 let attrs = attributes.iter().cloned().map(Symbol::intern).collect();
-                let derive = SyntaxExtension::CustomDerive(
-                    Box::new(CustomDerive::new(expand, attrs))
+                let derive = SyntaxExtension::ProcMacroDerive(
+                    Box::new(ProcMacroDerive::new(expand, attrs))
                 );
                 self.0.push((Symbol::intern(trait_name), Rc::new(derive)));
             }
diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs
index eb6c7f4bed5..4679b6be88b 100644
--- a/src/librustc_resolve/build_reduced_graph.rs
+++ b/src/librustc_resolve/build_reduced_graph.rs
@@ -559,7 +559,7 @@ impl<'a> Resolver<'a> {
                       "an `extern crate` loading macros must be at the crate root");
         } else if !self.use_extern_macros && !used &&
                   self.session.cstore.dep_kind(module.def_id().unwrap().krate).macros_only() {
-            let msg = "custom derive crates and `#[no_link]` crates have no effect without \
+            let msg = "proc macro crates and `#[no_link]` crates have no effect without \
                        `#[macro_use]`";
             self.session.span_warn(item.span, msg);
             used = true; // Avoid the normal unused extern crate warning
diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs
index 682b3ff834f..ea3112b2463 100644
--- a/src/librustc_resolve/macros.rs
+++ b/src/librustc_resolve/macros.rs
@@ -250,6 +250,32 @@ impl<'a> base::Resolver for Resolver<'a> {
         }
         result
     }
+
+    fn resolve_builtin_macro(&mut self, tname: Name) -> Result<Rc<SyntaxExtension>, Determinacy> {
+        match self.builtin_macros.get(&tname).cloned() {
+            Some(binding) => Ok(binding.get_macro(self)),
+            None => Err(Determinacy::Undetermined),
+        }
+    }
+
+    fn resolve_derive_macro(&mut self, scope: Mark, path: &ast::Path, force: bool)
+                            -> Result<Rc<SyntaxExtension>, Determinacy> {
+        let ast::Path { span, .. } = *path;
+        match self.resolve_macro(scope, path, false) {
+            Ok(ext) => match *ext {
+                SyntaxExtension::BuiltinDerive(..) |
+                SyntaxExtension::ProcMacroDerive(..) => Ok(ext),
+                _ => Err(Determinacy::Determined),
+            },
+            Err(Determinacy::Undetermined) if force => {
+                let msg = format!("cannot find derive macro `{}` in this scope", path);
+                let mut err = self.session.struct_span_err(span, &msg);
+                err.emit();
+                Err(Determinacy::Determined)
+            },
+            Err(err) => Err(err),
+        }
+    }
 }
 
 impl<'a> Resolver<'a> {
diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs
index edf74e1fe19..9a717b86d09 100644
--- a/src/libsyntax/ext/base.rs
+++ b/src/libsyntax/ext/base.rs
@@ -10,7 +10,7 @@
 
 pub use self::SyntaxExtension::{MultiDecorator, MultiModifier, NormalTT, IdentTT};
 
-use ast::{self, Attribute, Name, PatKind};
+use ast::{self, Attribute, Name, PatKind, MetaItem};
 use attr::HasAttrs;
 use codemap::{self, CodeMap, ExpnInfo, Spanned, respan};
 use syntax_pos::{Span, ExpnId, NO_EXPANSION};
@@ -471,6 +471,9 @@ impl MacResult for DummyResult {
     }
 }
 
+pub type BuiltinDeriveFn =
+    for<'cx> fn(&'cx mut ExtCtxt, Span, &MetaItem, &Annotatable, &mut FnMut(Annotatable));
+
 /// An enum representing the different kinds of syntax extensions.
 pub enum SyntaxExtension {
     /// A syntax extension that is attached to an item and creates new items
@@ -507,7 +510,14 @@ pub enum SyntaxExtension {
     ///
     IdentTT(Box<IdentMacroExpander>, Option<Span>, bool),
 
-    CustomDerive(Box<MultiItemModifier>),
+    /// An attribute-like procedural macro. TokenStream -> TokenStream.
+    /// The input is the annotated item.
+    /// Allows generating code to implement a Trait for a given struct
+    /// or enum item.
+    ProcMacroDerive(Box<MultiItemModifier>),
+
+    /// An attribute-like procedural macro that derives a builtin trait.
+    BuiltinDerive(BuiltinDeriveFn),
 }
 
 pub type NamedSyntaxExtension = (Name, SyntaxExtension);
@@ -526,6 +536,9 @@ pub trait Resolver {
     fn find_attr_invoc(&mut self, attrs: &mut Vec<Attribute>) -> Option<Attribute>;
     fn resolve_macro(&mut self, scope: Mark, path: &ast::Path, force: bool)
                      -> Result<Rc<SyntaxExtension>, Determinacy>;
+    fn resolve_builtin_macro(&mut self, tname: Name) -> Result<Rc<SyntaxExtension>, Determinacy>;
+    fn resolve_derive_macro(&mut self, scope: Mark, path: &ast::Path, force: bool)
+                            -> Result<Rc<SyntaxExtension>, Determinacy>;
 }
 
 #[derive(Copy, Clone, Debug)]
@@ -552,6 +565,13 @@ impl Resolver for DummyResolver {
                      -> Result<Rc<SyntaxExtension>, Determinacy> {
         Err(Determinacy::Determined)
     }
+    fn resolve_builtin_macro(&mut self, _tname: Name) -> Result<Rc<SyntaxExtension>, Determinacy> {
+        Err(Determinacy::Determined)
+    }
+    fn resolve_derive_macro(&mut self, _scope: Mark, _path: &ast::Path, _force: bool)
+                            -> Result<Rc<SyntaxExtension>, Determinacy> {
+        Err(Determinacy::Determined)
+    }
 }
 
 #[derive(Clone)]
diff --git a/src/libsyntax/ext/derive.rs b/src/libsyntax/ext/derive.rs
new file mode 100644
index 00000000000..946448eaaee
--- /dev/null
+++ b/src/libsyntax/ext/derive.rs
@@ -0,0 +1,218 @@
+// Copyright 2012-2017 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.
+
+use ast::Name;
+use attr;
+use ast::{self, NestedMetaItem}; use ext::base::{ExtCtxt, SyntaxExtension};
+use codemap;
+use ext::build::AstBuilder;
+use feature_gate;
+use symbol::Symbol;
+use syntax_pos::Span;
+
+pub fn derive_attr_trait<'a>(cx: &mut ExtCtxt, attr: &'a ast::Attribute)
+                             -> Option<&'a NestedMetaItem> {
+    if attr.name() != "derive" {
+        return None;
+    }
+    if attr.value_str().is_some() {
+        cx.span_err(attr.span, "unexpected value in `derive`");
+        return None;
+    }
+
+    let traits = attr.meta_item_list().unwrap_or(&[]);
+
+    if traits.is_empty() {
+        cx.span_warn(attr.span, "empty trait list in `derive`");
+        return None;
+    }
+
+    return traits.get(0);
+}
+
+pub fn verify_derive_attrs(cx: &mut ExtCtxt, attrs: &[ast::Attribute]) {
+    for attr in attrs {
+        if attr.name() != "derive" {
+            continue;
+        }
+
+        if attr.value_str().is_some() {
+            cx.span_err(attr.span, "unexpected value in `derive`");
+        }
+
+        let traits = attr.meta_item_list().unwrap_or(&[]).to_owned();
+
+        if traits.is_empty() {
+            cx.span_warn(attr.span, "empty trait list in `derive`");
+            attr::mark_used(&attr);
+            continue;
+        }
+        for titem in traits {
+            if titem.word().is_none() {
+                cx.span_err(titem.span, "malformed `derive` entry");
+            }
+        }
+    }
+}
+
+#[derive(PartialEq, Debug, Clone, Copy)]
+pub enum DeriveType {
+    Legacy,
+    ProcMacro,
+    Builtin
+}
+
+impl DeriveType {
+    // Classify a derive trait name by resolving the macro.
+    pub fn classify(cx: &mut ExtCtxt, tname: Name) -> DeriveType {
+        let legacy_derive_name = Symbol::intern(&format!("derive_{}", tname));
+
+        if let Ok(_) = cx.resolver.resolve_builtin_macro(legacy_derive_name) {
+            return DeriveType::Legacy;
+        }
+
+        match cx.resolver.resolve_builtin_macro(tname) {
+            Ok(ext) => match *ext {
+                SyntaxExtension::BuiltinDerive(..) => DeriveType::Builtin,
+                _ => DeriveType::ProcMacro,
+            },
+            Err(_) => DeriveType::ProcMacro,
+        }
+    }
+}
+
+pub fn get_derive_attr(cx: &mut ExtCtxt, attrs: &mut Vec<ast::Attribute>,
+                       derive_type: DeriveType) -> Option<ast::Attribute> {
+    for i in 0..attrs.len() {
+        if attrs[i].name() != "derive" {
+            continue;
+        }
+
+        if attrs[i].value_str().is_some() {
+            continue;
+        }
+
+        let mut traits = attrs[i].meta_item_list().unwrap_or(&[]).to_owned();
+
+        // First, weed out malformed #[derive]
+        traits.retain(|titem| titem.word().is_some());
+
+        let mut titem = None;
+
+        // See if we can find a matching trait.
+        for j in 0..traits.len() {
+            let tname = match traits[j].name() {
+                Some(tname) => tname,
+                _ => continue,
+            };
+
+            if DeriveType::classify(cx, tname) == derive_type {
+                titem = Some(traits.remove(j));
+                break;
+            }
+        }
+
+        // If we find a trait, remove the trait from the attribute.
+        if let Some(titem) = titem {
+            if traits.len() == 0 {
+                attrs.remove(i);
+            } else {
+                let derive = Symbol::intern("derive");
+                let mitem = cx.meta_list(titem.span, derive, traits);
+                attrs[i] = cx.attribute(titem.span, mitem);
+            }
+            let derive = Symbol::intern("derive");
+            let mitem = cx.meta_list(titem.span, derive, vec![titem]);
+            return Some(cx.attribute(mitem.span, mitem));
+        }
+    }
+    return None;
+}
+
+fn allow_unstable(cx: &mut ExtCtxt, span: Span, attr_name: &str) -> Span {
+    Span {
+        expn_id: cx.codemap().record_expansion(codemap::ExpnInfo {
+            call_site: span,
+            callee: codemap::NameAndSpan {
+                format: codemap::MacroAttribute(Symbol::intern(attr_name)),
+                span: Some(span),
+                allow_internal_unstable: true,
+            },
+        }),
+        ..span
+    }
+}
+
+pub fn add_derived_markers(cx: &mut ExtCtxt, attrs: &mut Vec<ast::Attribute>) {
+    if attrs.is_empty() {
+        return;
+    }
+
+    let titems = attrs.iter().filter(|a| {
+        a.name() == "derive"
+    }).flat_map(|a| {
+        a.meta_item_list().unwrap_or(&[]).iter()
+    }).filter_map(|titem| {
+        titem.name()
+    }).collect::<Vec<_>>();
+
+    let span = attrs[0].span;
+
+    if !attrs.iter().any(|a| a.name() == "structural_match") &&
+       titems.iter().any(|t| *t == "PartialEq") && titems.iter().any(|t| *t == "Eq") {
+        let structural_match = Symbol::intern("structural_match");
+        let span = allow_unstable(cx, span, "derive(PartialEq, Eq)");
+        let meta = cx.meta_word(span, structural_match);
+        attrs.push(cx.attribute(span, meta));
+    }
+
+    if !attrs.iter().any(|a| a.name() == "rustc_copy_clone_marker") &&
+       titems.iter().any(|t| *t == "Copy") && titems.iter().any(|t| *t == "Clone") {
+        let structural_match = Symbol::intern("rustc_copy_clone_marker");
+        let span = allow_unstable(cx, span, "derive(Copy, Clone)");
+        let meta = cx.meta_word(span, structural_match);
+        attrs.push(cx.attribute(span, meta));
+    }
+}
+
+pub fn find_derive_attr(cx: &mut ExtCtxt, attrs: &mut Vec<ast::Attribute>)
+                        -> Option<ast::Attribute> {
+    verify_derive_attrs(cx, attrs);
+    get_derive_attr(cx, attrs, DeriveType::Legacy).and_then(|a| {
+        let titem = derive_attr_trait(cx, &a);
+        titem.and_then(|titem| {
+            let tword = titem.word().unwrap();
+            let tname = tword.name();
+            if !cx.ecfg.enable_custom_derive() {
+                feature_gate::emit_feature_err(
+                    &cx.parse_sess,
+                    "custom_derive",
+                    titem.span,
+                    feature_gate::GateIssue::Language,
+                    feature_gate::EXPLAIN_CUSTOM_DERIVE
+                );
+                None
+            } else {
+                let name = Symbol::intern(&format!("derive_{}", tname));
+                if !cx.resolver.is_whitelisted_legacy_custom_derive(name) {
+                    cx.span_warn(titem.span,
+                                 feature_gate::EXPLAIN_DEPR_CUSTOM_DERIVE);
+                }
+                let mitem = cx.meta_word(titem.span, name);
+                Some(cx.attribute(mitem.span, mitem))
+            }
+        })
+    }).or_else(|| {
+        get_derive_attr(cx, attrs, DeriveType::ProcMacro)
+    }).or_else(|| {
+        add_derived_markers(cx, attrs);
+        get_derive_attr(cx, attrs, DeriveType::Builtin)
+    })
+}
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index 226625ebc8e..8e7f8830eaf 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -8,26 +8,27 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use ast::{Block, Ident, Mac_, PatKind};
+use ast::{self, Block, Ident, Mac_, PatKind};
 use ast::{Name, MacStmtStyle, StmtKind, ItemKind};
-use ast;
-use ext::hygiene::Mark;
-use ext::placeholders::{placeholder, PlaceholderExpander};
 use attr::{self, HasAttrs};
 use codemap::{ExpnInfo, NameAndSpan, MacroBang, MacroAttribute};
-use syntax_pos::{self, Span, ExpnId};
 use config::{is_test_or_bench, StripUnconfigured};
 use ext::base::*;
+use ext::derive::{find_derive_attr, derive_attr_trait};
+use ext::hygiene::Mark;
+use ext::placeholders::{placeholder, PlaceholderExpander};
 use feature_gate::{self, Features};
 use fold;
 use fold::*;
-use parse::{ParseSess, DirectoryOwnership, PResult, filemap_to_tts};
 use parse::parser::Parser;
 use parse::token;
+use parse::{ParseSess, DirectoryOwnership, PResult, filemap_to_tts};
 use print::pprust;
 use ptr::P;
 use std_inject;
+use symbol::Symbol;
 use symbol::keywords;
+use syntax_pos::{self, Span, ExpnId};
 use tokenstream::{TokenTree, TokenStream};
 use util::small_vector::SmallVector;
 use visit::Visitor;
@@ -166,6 +167,10 @@ pub enum InvocationKind {
         attr: ast::Attribute,
         item: Annotatable,
     },
+    Derive {
+        attr: ast::Attribute,
+        item: Annotatable,
+    },
 }
 
 impl Invocation {
@@ -173,6 +178,7 @@ impl Invocation {
         match self.kind {
             InvocationKind::Bang { span, .. } => span,
             InvocationKind::Attr { ref attr, .. } => attr.span,
+            InvocationKind::Derive { ref attr, .. } => attr.span,
         }
     }
 }
@@ -250,6 +256,13 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                     let path = ast::Path::from_ident(attr.span, ident);
                     self.cx.resolver.resolve_macro(scope, &path, force)
                 }
+                InvocationKind::Derive { ref attr, .. } => {
+                    let titem = derive_attr_trait(self.cx, &attr).unwrap();
+                    let tname = titem.name().expect("Expected derive macro name");
+                    let ident = Ident::with_empty_ctxt(tname);
+                    let path = ast::Path::from_ident(attr.span, ident);
+                    self.cx.resolver.resolve_derive_macro(scope, &path, force)
+                }
             };
             let ext = match resolution {
                 Ok(ext) => Some(ext),
@@ -330,6 +343,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
         match invoc.kind {
             InvocationKind::Bang { .. } => self.expand_bang_invoc(invoc, ext),
             InvocationKind::Attr { .. } => self.expand_attr_invoc(invoc, ext),
+            InvocationKind::Derive { .. } => self.expand_derive_invoc(invoc, ext),
         }
     }
 
@@ -370,7 +384,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                 let tok_result = mac.expand(self.cx, attr.span, attr_toks, item_toks);
                 self.parse_expansion(tok_result, kind, name, attr.span)
             }
-            SyntaxExtension::CustomDerive(_) => {
+            SyntaxExtension::ProcMacroDerive(..) | SyntaxExtension::BuiltinDerive(..) => {
                 self.cx.span_err(attr.span, &format!("`{}` is a derive mode", name));
                 kind.dummy(attr.span)
             }
@@ -440,7 +454,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                 return kind.dummy(span);
             }
 
-            SyntaxExtension::CustomDerive(..) => {
+            SyntaxExtension::ProcMacroDerive(..) | SyntaxExtension::BuiltinDerive(..) => {
                 self.cx.span_err(path.span, &format!("`{}` is a derive mode", extname));
                 return kind.dummy(span);
             }
@@ -486,6 +500,71 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
         })
     }
 
+    /// Expand a derive invocation. Returns the result of expansion.
+    fn expand_derive_invoc(&mut self, invoc: Invocation, ext: Rc<SyntaxExtension>) -> Expansion {
+        let Invocation { expansion_kind: kind, .. } = invoc;
+        let (attr, item) = match invoc.kind {
+            InvocationKind::Derive { attr, item } => (attr, item),
+            _ => unreachable!(),
+        };
+
+        attr::mark_used(&attr);
+        let titem = derive_attr_trait(self.cx, &attr).unwrap();
+        let tname = ast::Ident::with_empty_ctxt(titem.name().unwrap());
+        let name = Symbol::intern(&format!("derive({})", tname));
+        let mitem = &attr.value;
+
+        self.cx.bt_push(ExpnInfo {
+            call_site: attr.span,
+            callee: NameAndSpan {
+                format: MacroAttribute(attr.name()),
+                span: Some(attr.span),
+                allow_internal_unstable: false,
+            }
+        });
+
+        match *ext {
+            SyntaxExtension::ProcMacroDerive(ref ext) => {
+                let span = Span {
+                    expn_id: self.cx.codemap().record_expansion(ExpnInfo {
+                        call_site: mitem.span,
+                        callee: NameAndSpan {
+                            format: MacroAttribute(Symbol::intern(&format!("derive({})", tname))),
+                            span: None,
+                            allow_internal_unstable: false,
+                        },
+                    }),
+                    ..mitem.span
+                };
+                return kind.expect_from_annotatables(ext.expand(self.cx, span, &mitem, item));
+            }
+            SyntaxExtension::BuiltinDerive(func) => {
+                let span = Span {
+                    expn_id: self.cx.codemap().record_expansion(ExpnInfo {
+                        call_site: titem.span,
+                        callee: NameAndSpan {
+                            format: MacroAttribute(name),
+                            span: None,
+                            allow_internal_unstable: true,
+                        },
+                    }),
+                    ..titem.span
+                };
+                let mut items = Vec::new();
+                func(self.cx, span, &mitem, &item, &mut |a| {
+                    items.push(a)
+                });
+                items.insert(0, item);
+                return kind.expect_from_annotatables(items);
+            }
+            _ => {
+                let msg = &format!("macro `{}` may not be used for derive attributes", name);
+                self.cx.span_err(attr.span, &msg);
+                kind.dummy(attr.span)
+            }
+        }
+    }
+
     fn parse_expansion(&mut self, toks: TokenStream, kind: ExpansionKind, name: Name, span: Span)
                        -> Expansion {
         let mut parser = self.cx.new_parser_from_tts(&toks.trees().cloned().collect::<Vec<_>>());
@@ -595,16 +674,31 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
 
     fn collect_attr(&mut self, attr: ast::Attribute, item: Annotatable, kind: ExpansionKind)
                     -> Expansion {
-        self.collect(kind, InvocationKind::Attr { attr: attr, item: item })
+        let invoc_kind = if attr.name() == "derive" {
+            if kind == ExpansionKind::TraitItems || kind == ExpansionKind::ImplItems {
+                self.cx.span_err(attr.span, "`derive` can be only be applied to items");
+                return kind.expect_from_annotatables(::std::iter::once(item));
+            }
+            InvocationKind::Derive { attr: attr, item: item }
+        } else {
+            InvocationKind::Attr { attr: attr, item: item }
+        };
+
+        self.collect(kind, invoc_kind)
     }
 
     // If `item` is an attr invocation, remove and return the macro attribute.
     fn classify_item<T: HasAttrs>(&mut self, mut item: T) -> (T, Option<ast::Attribute>) {
         let mut attr = None;
+
         item = item.map_attrs(|mut attrs| {
-            attr = self.cx.resolver.find_attr_invoc(&mut attrs);
+            attr = self.cx.resolver.find_attr_invoc(&mut attrs).or_else(|| {
+                find_derive_attr(self.cx, &mut attrs)
+            });
+
             attrs
         });
+
         (item, attr)
     }
 
diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs
index 25be9e91a6a..87a03adf6b7 100644
--- a/src/libsyntax/lib.rs
+++ b/src/libsyntax/lib.rs
@@ -128,6 +128,7 @@ pub mod print {
 pub mod ext {
     pub mod base;
     pub mod build;
+    pub mod derive;
     pub mod expand;
     pub mod placeholders;
     pub mod hygiene;
diff --git a/src/libsyntax_ext/deriving/custom.rs b/src/libsyntax_ext/deriving/custom.rs
index 2ce6fc03f77..974e86a5741 100644
--- a/src/libsyntax_ext/deriving/custom.rs
+++ b/src/libsyntax_ext/deriving/custom.rs
@@ -32,18 +32,18 @@ impl<'a> Visitor<'a> for MarkAttrs<'a> {
     fn visit_mac(&mut self, _mac: &Mac) {}
 }
 
-pub struct CustomDerive {
+pub struct ProcMacroDerive {
     inner: fn(TokenStream) -> TokenStream,
     attrs: Vec<ast::Name>,
 }
 
-impl CustomDerive {
-    pub fn new(inner: fn(TokenStream) -> TokenStream, attrs: Vec<ast::Name>) -> CustomDerive {
-        CustomDerive { inner: inner, attrs: attrs }
+impl ProcMacroDerive {
+    pub fn new(inner: fn(TokenStream) -> TokenStream, attrs: Vec<ast::Name>) -> ProcMacroDerive {
+        ProcMacroDerive { inner: inner, attrs: attrs }
     }
 }
 
-impl MultiItemModifier for CustomDerive {
+impl MultiItemModifier for ProcMacroDerive {
     fn expand(&self,
               ecx: &mut ExtCtxt,
               span: Span,
@@ -54,7 +54,7 @@ impl MultiItemModifier for CustomDerive {
             Annotatable::Item(item) => item,
             Annotatable::ImplItem(_) |
             Annotatable::TraitItem(_) => {
-                ecx.span_err(span, "custom derive attributes may only be \
+                ecx.span_err(span, "proc-macro derives may only be \
                                     applied to struct/enum items");
                 return Vec::new()
             }
@@ -63,7 +63,7 @@ impl MultiItemModifier for CustomDerive {
             ItemKind::Struct(..) |
             ItemKind::Enum(..) => {},
             _ => {
-                ecx.span_err(span, "custom derive attributes may only be \
+                ecx.span_err(span, "proc-macro derives may only be \
                                     applied to struct/enum items");
                 return Vec::new()
             }
@@ -81,7 +81,7 @@ impl MultiItemModifier for CustomDerive {
         let stream = match res {
             Ok(stream) => stream,
             Err(e) => {
-                let msg = "custom derive attribute panicked";
+                let msg = "proc-macro derive panicked";
                 let mut err = ecx.struct_span_fatal(span, msg);
                 if let Some(s) = e.downcast_ref::<String>() {
                     err.help(&format!("message: {}", s));
@@ -100,7 +100,7 @@ impl MultiItemModifier for CustomDerive {
                 Ok(new_items) => new_items,
                 Err(_) => {
                     // FIXME: handle this better
-                    let msg = "custom derive produced unparseable tokens";
+                    let msg = "proc-macro derive produced unparseable tokens";
                     ecx.struct_span_fatal(span, msg).emit();
                     panic!(FatalError);
                 }
diff --git a/src/libsyntax_ext/deriving/decodable.rs b/src/libsyntax_ext/deriving/decodable.rs
index e2634c60dca..498f2348b80 100644
--- a/src/libsyntax_ext/deriving/decodable.rs
+++ b/src/libsyntax_ext/deriving/decodable.rs
@@ -13,6 +13,7 @@
 use deriving;
 use deriving::generic::*;
 use deriving::generic::ty::*;
+use deriving::warn_if_deprecated;
 
 use syntax::ast;
 use syntax::ast::{Expr, MetaItem, Mutability};
@@ -35,6 +36,7 @@ pub fn expand_deriving_decodable(cx: &mut ExtCtxt,
                                  mitem: &MetaItem,
                                  item: &Annotatable,
                                  push: &mut FnMut(Annotatable)) {
+    warn_if_deprecated(cx, span, "Decodable");
     expand_deriving_decodable_imp(cx, span, mitem, item, push, "serialize")
 }
 
diff --git a/src/libsyntax_ext/deriving/encodable.rs b/src/libsyntax_ext/deriving/encodable.rs
index 092738ab8a0..9d155c22ad0 100644
--- a/src/libsyntax_ext/deriving/encodable.rs
+++ b/src/libsyntax_ext/deriving/encodable.rs
@@ -91,6 +91,7 @@
 use deriving;
 use deriving::generic::*;
 use deriving::generic::ty::*;
+use deriving::warn_if_deprecated;
 
 use syntax::ast::{Expr, ExprKind, MetaItem, Mutability};
 use syntax::ext::base::{Annotatable, ExtCtxt};
@@ -112,6 +113,7 @@ pub fn expand_deriving_encodable(cx: &mut ExtCtxt,
                                  mitem: &MetaItem,
                                  item: &Annotatable,
                                  push: &mut FnMut(Annotatable)) {
+    warn_if_deprecated(cx, span, "Encodable");
     expand_deriving_encodable_imp(cx, span, mitem, item, push, "serialize")
 }
 
diff --git a/src/libsyntax_ext/deriving/mod.rs b/src/libsyntax_ext/deriving/mod.rs
index 096f6dfd5d8..3bceb02f3d6 100644
--- a/src/libsyntax_ext/deriving/mod.rs
+++ b/src/libsyntax_ext/deriving/mod.rs
@@ -10,12 +10,11 @@
 
 //! The compiler code necessary to implement the `#[derive]` extensions.
 
-use syntax::ast::{self, MetaItem};
-use syntax::attr::HasAttrs;
+use std::rc::Rc;
+use syntax::ast;
 use syntax::codemap;
-use syntax::ext::base::{Annotatable, ExtCtxt, SyntaxExtension};
+use syntax::ext::base::{Annotatable, ExtCtxt, SyntaxExtension, Resolver};
 use syntax::ext::build::AstBuilder;
-use syntax::feature_gate;
 use syntax::ptr::P;
 use syntax::symbol::Symbol;
 use syntax_pos::Span;
@@ -89,234 +88,6 @@ fn allow_unstable(cx: &mut ExtCtxt, span: Span, attr_name: &str) -> Span {
     }
 }
 
-pub fn expand_derive(cx: &mut ExtCtxt,
-                     span: Span,
-                     mitem: &MetaItem,
-                     annotatable: Annotatable)
-                     -> Vec<Annotatable> {
-    debug!("expand_derive: span = {:?}", span);
-    debug!("expand_derive: mitem = {:?}", mitem);
-    debug!("expand_derive: annotatable input  = {:?}", annotatable);
-    let mut item = match annotatable {
-        Annotatable::Item(item) => item,
-        other => {
-            cx.span_err(span, "`derive` can only be applied to items");
-            return vec![other]
-        }
-    };
-
-    let derive = Symbol::intern("derive");
-    let mut derive_attrs = Vec::new();
-    item = item.map_attrs(|attrs| {
-        let partition = attrs.into_iter().partition(|attr| attr.name() == derive);
-        derive_attrs = partition.0;
-        partition.1
-    });
-
-    // Expand `#[derive]`s after other attribute macro invocations.
-    if cx.resolver.find_attr_invoc(&mut item.attrs.clone()).is_some() {
-        return vec![Annotatable::Item(item.map_attrs(|mut attrs| {
-            attrs.push(cx.attribute(span, mitem.clone()));
-            attrs.extend(derive_attrs);
-            attrs
-        }))];
-    }
-
-    let get_traits = |mitem: &MetaItem, cx: &ExtCtxt| {
-        if mitem.value_str().is_some() {
-            cx.span_err(mitem.span, "unexpected value in `derive`");
-        }
-
-        let traits = mitem.meta_item_list().unwrap_or(&[]).to_owned();
-        if traits.is_empty() {
-            cx.span_warn(mitem.span, "empty trait list in `derive`");
-        }
-        traits
-    };
-
-    let mut traits = get_traits(mitem, cx);
-    for derive_attr in derive_attrs {
-        traits.extend(get_traits(&derive_attr.value, cx));
-    }
-
-    // First, weed out malformed #[derive]
-    traits.retain(|titem| {
-        if titem.word().is_none() {
-            cx.span_err(titem.span, "malformed `derive` entry");
-            false
-        } else {
-            true
-        }
-    });
-
-    // Next, check for old-style #[derive(Foo)]
-    //
-    // These all get expanded to `#[derive_Foo]` and will get expanded first. If
-    // we actually add any attributes here then we return to get those expanded
-    // and then eventually we'll come back to finish off the other derive modes.
-    let mut new_attributes = Vec::new();
-    traits.retain(|titem| {
-        let tword = titem.word().unwrap();
-        let tname = tword.name();
-
-        if is_builtin_trait(tname) || {
-            let derive_mode = ast::Path::from_ident(titem.span, ast::Ident::with_empty_ctxt(tname));
-            cx.resolver.resolve_macro(cx.current_expansion.mark, &derive_mode, false).map(|ext| {
-                if let SyntaxExtension::CustomDerive(_) = *ext { true } else { false }
-            }).unwrap_or(false)
-        } {
-            return true;
-        }
-
-        if !cx.ecfg.enable_custom_derive() {
-            feature_gate::emit_feature_err(&cx.parse_sess,
-                                           "custom_derive",
-                                           titem.span,
-                                           feature_gate::GateIssue::Language,
-                                           feature_gate::EXPLAIN_CUSTOM_DERIVE);
-        } else {
-            let name = Symbol::intern(&format!("derive_{}", tname));
-            if !cx.resolver.is_whitelisted_legacy_custom_derive(name) {
-                cx.span_warn(titem.span, feature_gate::EXPLAIN_DEPR_CUSTOM_DERIVE);
-            }
-            let mitem = cx.meta_word(titem.span, name);
-            new_attributes.push(cx.attribute(mitem.span, mitem));
-        }
-        false
-    });
-    if new_attributes.len() > 0 {
-        item = item.map(|mut i| {
-            i.attrs.extend(new_attributes);
-            if traits.len() > 0 {
-                let list = cx.meta_list(mitem.span, derive, traits);
-                i.attrs.push(cx.attribute(mitem.span, list));
-            }
-            i
-        });
-        return vec![Annotatable::Item(item)]
-    }
-
-    // Now check for macros-1.1 style custom #[derive].
-    //
-    // Expand each of them in order given, but *before* we expand any built-in
-    // derive modes. The logic here is to:
-    //
-    // 1. Collect the remaining `#[derive]` annotations into a list. If
-    //    there are any left, attach a `#[derive]` attribute to the item
-    //    that we're currently expanding with the remaining derive modes.
-    // 2. Manufacture a `#[derive(Foo)]` attribute to pass to the expander.
-    // 3. Expand the current item we're expanding, getting back a list of
-    //    items that replace it.
-    // 4. Extend the returned list with the current list of items we've
-    //    collected so far.
-    // 5. Return everything!
-    //
-    // If custom derive extensions end up threading through the `#[derive]`
-    // attribute, we'll get called again later on to continue expanding
-    // those modes.
-    let macros_11_derive = traits.iter()
-                                 .cloned()
-                                 .enumerate()
-                                 .filter(|&(_, ref name)| !is_builtin_trait(name.name().unwrap()))
-                                 .next();
-    if let Some((i, titem)) = macros_11_derive {
-        let tname = ast::Ident::with_empty_ctxt(titem.name().unwrap());
-        let path = ast::Path::from_ident(titem.span, tname);
-        let ext = cx.resolver.resolve_macro(cx.current_expansion.mark, &path, false).unwrap();
-
-        traits.remove(i);
-        if traits.len() > 0 {
-            item = item.map(|mut i| {
-                let list = cx.meta_list(mitem.span, derive, traits);
-                i.attrs.push(cx.attribute(mitem.span, list));
-                i
-            });
-        }
-        let titem = cx.meta_list_item_word(titem.span, titem.name().unwrap());
-        let mitem = cx.meta_list(titem.span, derive, vec![titem]);
-        let item = Annotatable::Item(item);
-
-        let span = Span {
-            expn_id: cx.codemap().record_expansion(codemap::ExpnInfo {
-                call_site: mitem.span,
-                callee: codemap::NameAndSpan {
-                    format: codemap::MacroAttribute(Symbol::intern(&format!("derive({})", tname))),
-                    span: None,
-                    allow_internal_unstable: false,
-                },
-            }),
-            ..mitem.span
-        };
-
-        if let SyntaxExtension::CustomDerive(ref ext) = *ext {
-            return ext.expand(cx, span, &mitem, item);
-        } else {
-            unreachable!()
-        }
-    }
-
-    // Ok, at this point we know that there are no old-style `#[derive_Foo]` nor
-    // any macros-1.1 style `#[derive(Foo)]`. Expand all built-in traits here.
-
-    // RFC #1445. `#[derive(PartialEq, Eq)]` adds a (trusted)
-    // `#[structural_match]` attribute.
-    let (partial_eq, eq) = (Symbol::intern("PartialEq"), Symbol::intern("Eq"));
-    if traits.iter().any(|t| t.name() == Some(partial_eq)) &&
-       traits.iter().any(|t| t.name() == Some(eq)) {
-        let structural_match = Symbol::intern("structural_match");
-        let span = allow_unstable(cx, span, "derive(PartialEq, Eq)");
-        let meta = cx.meta_word(span, structural_match);
-        item = item.map(|mut i| {
-            i.attrs.push(cx.attribute(span, meta));
-            i
-        });
-    }
-
-    // RFC #1521. `Clone` can assume that `Copy` types' clone implementation is
-    // the same as the copy implementation.
-    //
-    // Add a marker attribute here picked up during #[derive(Clone)]
-    let (copy, clone) = (Symbol::intern("Copy"), Symbol::intern("Clone"));
-    if traits.iter().any(|t| t.name() == Some(clone)) &&
-       traits.iter().any(|t| t.name() == Some(copy)) {
-        let marker = Symbol::intern("rustc_copy_clone_marker");
-        let span = allow_unstable(cx, span, "derive(Copy, Clone)");
-        let meta = cx.meta_word(span, marker);
-        item = item.map(|mut i| {
-            i.attrs.push(cx.attribute(span, meta));
-            i
-        });
-    }
-
-    let mut items = Vec::new();
-    for titem in traits.iter() {
-        let tname = titem.word().unwrap().name();
-        let name = Symbol::intern(&format!("derive({})", tname));
-        let mitem = cx.meta_word(titem.span, name);
-
-        let span = Span {
-            expn_id: cx.codemap().record_expansion(codemap::ExpnInfo {
-                call_site: titem.span,
-                callee: codemap::NameAndSpan {
-                    format: codemap::MacroAttribute(name),
-                    span: None,
-                    allow_internal_unstable: true,
-                },
-            }),
-            ..titem.span
-        };
-
-        let my_item = Annotatable::Item(item);
-        expand_builtin(&tname.as_str(), cx, span, &mitem, &my_item, &mut |a| {
-            items.push(a);
-        });
-        item = my_item.expect_item();
-    }
-
-    items.insert(0, Annotatable::Item(item));
-    return items
-}
-
 macro_rules! derive_traits {
     ($( $name:expr => $func:path, )+) => {
         pub fn is_builtin_trait(name: ast::Name) -> bool {
@@ -326,21 +97,13 @@ macro_rules! derive_traits {
             }
         }
 
-        fn expand_builtin(name: &str,
-                          ecx: &mut ExtCtxt,
-                          span: Span,
-                          mitem: &MetaItem,
-                          item: &Annotatable,
-                          push: &mut FnMut(Annotatable)) {
-            match name {
-                $(
-                    $name => {
-                        warn_if_deprecated(ecx, span, $name);
-                        $func(ecx, span, mitem, item, push);
-                    }
-                )*
-                _ => panic!("not a builtin derive mode: {}", name),
-            }
+        pub fn register_builtin_derives(resolver: &mut Resolver) {
+            $(
+                resolver.add_ext(
+                    ast::Ident::with_empty_ctxt(Symbol::intern($name)),
+                    Rc::new(SyntaxExtension::BuiltinDerive($func))
+                );
+            )*
         }
     }
 }
diff --git a/src/libsyntax_ext/lib.rs b/src/libsyntax_ext/lib.rs
index ebec23d0901..7533171b085 100644
--- a/src/libsyntax_ext/lib.rs
+++ b/src/libsyntax_ext/lib.rs
@@ -24,7 +24,6 @@
 #![feature(staged_api)]
 
 extern crate fmt_macros;
-#[macro_use]
 extern crate log;
 #[macro_use]
 extern crate syntax;
@@ -51,12 +50,14 @@ pub mod proc_macro_impl;
 
 use std::rc::Rc;
 use syntax::ast;
-use syntax::ext::base::{MacroExpanderFn, NormalTT, MultiModifier, NamedSyntaxExtension};
+use syntax::ext::base::{MacroExpanderFn, NormalTT, NamedSyntaxExtension};
 use syntax::symbol::Symbol;
 
 pub fn register_builtins(resolver: &mut syntax::ext::base::Resolver,
                          user_exts: Vec<NamedSyntaxExtension>,
                          enable_quotes: bool) {
+    deriving::register_builtin_derives(resolver);
+
     let mut register = |name, ext| {
         resolver.add_ext(ast::Ident::with_empty_ctxt(name), Rc::new(ext));
     };
@@ -112,8 +113,6 @@ pub fn register_builtins(resolver: &mut syntax::ext::base::Resolver,
     register(Symbol::intern("format_args"),
              NormalTT(Box::new(format::expand_format_args), None, true));
 
-    register(Symbol::intern("derive"), MultiModifier(Box::new(deriving::expand_derive)));
-
     for (name, ext) in user_exts {
         register(name, ext);
     }
diff --git a/src/libsyntax_ext/proc_macro_registrar.rs b/src/libsyntax_ext/proc_macro_registrar.rs
index c8af16e9242..325f09a83dd 100644
--- a/src/libsyntax_ext/proc_macro_registrar.rs
+++ b/src/libsyntax_ext/proc_macro_registrar.rs
@@ -27,7 +27,7 @@ use syntax_pos::{Span, DUMMY_SP};
 
 use deriving;
 
-struct CustomDerive {
+struct ProcMacroDerive {
     trait_name: ast::Name,
     function_name: Ident,
     span: Span,
@@ -40,7 +40,7 @@ struct AttrProcMacro {
 }
 
 struct CollectProcMacros<'a> {
-    derives: Vec<CustomDerive>,
+    derives: Vec<ProcMacroDerive>,
     attr_macros: Vec<AttrProcMacro>,
     in_root: bool,
     handler: &'a errors::Handler,
@@ -176,7 +176,7 @@ impl<'a> CollectProcMacros<'a> {
         };
 
         if self.in_root && item.vis == ast::Visibility::Public {
-            self.derives.push(CustomDerive {
+            self.derives.push(ProcMacroDerive {
                 span: item.span,
                 trait_name: trait_name,
                 function_name: item.ident,
@@ -319,7 +319,7 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> {
 //          }
 //      }
 fn mk_registrar(cx: &mut ExtCtxt,
-                custom_derives: &[CustomDerive],
+                custom_derives: &[ProcMacroDerive],
                 custom_attrs: &[AttrProcMacro]) -> P<ast::Item> {
     let eid = cx.codemap().record_expansion(ExpnInfo {
         call_site: DUMMY_SP,
diff --git a/src/test/compile-fail-fulldeps/proc-macro/derive-bad.rs b/src/test/compile-fail-fulldeps/proc-macro/derive-bad.rs
index a5359946c09..42fad803bfa 100644
--- a/src/test/compile-fail-fulldeps/proc-macro/derive-bad.rs
+++ b/src/test/compile-fail-fulldeps/proc-macro/derive-bad.rs
@@ -16,7 +16,7 @@ extern crate derive_bad;
 #[derive(
     A
 )]
-//~^^ ERROR: custom derive produced unparseable tokens
+//~^^ ERROR: proc-macro derive produced unparseable tokens
 struct A;
 
 fn main() {}
diff --git a/src/test/compile-fail-fulldeps/proc-macro/load-panic.rs b/src/test/compile-fail-fulldeps/proc-macro/load-panic.rs
index f9906b650fb..c483c048b41 100644
--- a/src/test/compile-fail-fulldeps/proc-macro/load-panic.rs
+++ b/src/test/compile-fail-fulldeps/proc-macro/load-panic.rs
@@ -14,7 +14,7 @@
 extern crate derive_panic;
 
 #[derive(A)]
-//~^ ERROR: custom derive attribute panicked
+//~^ ERROR: proc-macro derive panicked
 //~| HELP: message: nope!
 struct Foo;
 
diff --git a/src/test/compile-fail-fulldeps/proc-macro/no-macro-use-attr.rs b/src/test/compile-fail-fulldeps/proc-macro/no-macro-use-attr.rs
index f61b8b4073b..e47a4aefb5e 100644
--- a/src/test/compile-fail-fulldeps/proc-macro/no-macro-use-attr.rs
+++ b/src/test/compile-fail-fulldeps/proc-macro/no-macro-use-attr.rs
@@ -13,7 +13,7 @@
 #![feature(rustc_attrs)]
 
 extern crate derive_a;
-//~^ WARN custom derive crates and `#[no_link]` crates have no effect without `#[macro_use]`
+//~^ WARN proc macro crates and `#[no_link]` crates have no effect without `#[macro_use]`
 
 #[rustc_error]
 fn main() {} //~ ERROR compilation successful
diff --git a/src/test/compile-fail/deriving-meta-unknown-trait.rs b/src/test/compile-fail/deriving-meta-unknown-trait.rs
index 596cc1e7d58..d388ece0844 100644
--- a/src/test/compile-fail/deriving-meta-unknown-trait.rs
+++ b/src/test/compile-fail/deriving-meta-unknown-trait.rs
@@ -11,7 +11,7 @@
 // ignore-tidy-linelength
 
 #[derive(Eqr)]
-//~^ ERROR `#[derive]` for custom traits is not stable enough for use. It is deprecated and will be removed in v1.15 (see issue #29644)
+//~^ ERROR cannot find derive macro `Eqr` in this scope
 struct Foo;
 
 pub fn main() {}
diff --git a/src/test/compile-fail/deriving-primitive.rs b/src/test/compile-fail/deriving-primitive.rs
index be822a173ab..97a39a46c19 100644
--- a/src/test/compile-fail/deriving-primitive.rs
+++ b/src/test/compile-fail/deriving-primitive.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#[derive(FromPrimitive)] //~ERROR `#[derive]` for custom traits is not stable
+#[derive(FromPrimitive)] //~ERROR cannot find derive macro `FromPrimitive` in this scope
 enum Foo {}
 
 fn main() {}
diff --git a/src/test/compile-fail/macro-error.rs b/src/test/compile-fail/macro-error.rs
index 4a6dbf014a1..f467ba3b1e1 100644
--- a/src/test/compile-fail/macro-error.rs
+++ b/src/test/compile-fail/macro-error.rs
@@ -16,5 +16,5 @@ fn main() {
     foo!(0); // Check that we report errors at macro definition, not expansion.
 
     let _: cfg!(foo) = (); //~ ERROR non-type macro in type position
-    derive!(); //~ ERROR `derive` can only be used in attributes
+    derive!(); //~ ERROR macro undefined: 'derive!'
 }
diff --git a/src/test/compile-fail/macros-nonfatal-errors.rs b/src/test/compile-fail/macros-nonfatal-errors.rs
index 723e936212a..3f710af8ac9 100644
--- a/src/test/compile-fail/macros-nonfatal-errors.rs
+++ b/src/test/compile-fail/macros-nonfatal-errors.rs
@@ -14,9 +14,11 @@
 #![feature(asm)]
 #![feature(trace_macros, concat_idents)]
 
-#[derive(Default, //~ ERROR
-           Zero)] //~ ERROR
-enum CantDeriveThose {}
+#[derive(Zero)] //~ ERROR
+struct CantDeriveThis;
+
+#[derive(Default)] //~ ERROR
+enum OrDeriveThis {}
 
 fn main() {
     doesnt_exist!(); //~ ERROR
diff --git a/src/test/compile-fail/no-link.rs b/src/test/compile-fail/no-link.rs
index d8e7411bded..f74ff55e2c0 100644
--- a/src/test/compile-fail/no-link.rs
+++ b/src/test/compile-fail/no-link.rs
@@ -12,7 +12,7 @@
 
 #[no_link]
 extern crate empty_struct;
-//~^ WARN custom derive crates and `#[no_link]` crates have no effect without `#[macro_use]`
+//~^ WARN proc macro crates and `#[no_link]` crates have no effect without `#[macro_use]`
 
 fn main() {
     empty_struct::XEmpty1; //~ ERROR cannot find value `XEmpty1` in module `empty_struct`
diff --git a/src/test/pretty/attr-derive.rs b/src/test/pretty/attr-derive.rs
new file mode 100644
index 00000000000..a1c581a1868
--- /dev/null
+++ b/src/test/pretty/attr-derive.rs
@@ -0,0 +1,43 @@
+// Copyright 2012 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:derive-foo.rs
+// ignore-stage1
+// pp-exact
+// Testing that both the inner item and next outer item are
+// preserved, and that the first outer item parsed in main is not
+// accidentally carried over to each inner function
+
+#[macro_use]
+extern crate derive_foo;
+
+#[derive(Foo)]
+struct X;
+
+#[derive(Foo)]
+#[Bar]
+struct Y;
+
+#[derive(Foo)]
+struct WithRef {
+    x: X,
+    #[Bar]
+    y: Y,
+}
+
+#[derive(Foo)]
+enum Enum {
+
+    #[Bar]
+    Asdf,
+    Qwerty,
+}
+
+fn main() { }
diff --git a/src/test/pretty/attr-variant-data.rs b/src/test/pretty/attr-variant-data.rs
deleted file mode 100644
index 1ffacaa9f5a..00000000000
--- a/src/test/pretty/attr-variant-data.rs
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2012 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.
-
-// pp-exact
-// Testing that both the inner item and next outer item are
-// preserved, and that the first outer item parsed in main is not
-// accidentally carried over to each inner function
-
-#![feature(custom_attribute)]
-#![feature(custom_derive)]
-
-#[derive(Serialize, Deserialize)]
-struct X;
-
-#[derive(Serialize, Deserialize)]
-struct WithRef<'a, T: 'a> {
-    #[serde(skip_deserializing)]
-    t: Option<&'a T>,
-    #[serde(serialize_with = "ser_x", deserialize_with = "de_x")]
-    x: X,
-}
-
-#[derive(Serialize, Deserialize)]
-enum EnumWith<T> {
-    Unit,
-    Newtype(
-            #[serde(serialize_with = "ser_x", deserialize_with = "de_x")]
-            X),
-    Tuple(T,
-          #[serde(serialize_with = "ser_x", deserialize_with = "de_x")]
-          X),
-    Struct {
-        t: T,
-        #[serde(serialize_with = "ser_x", deserialize_with = "de_x")]
-        x: X,
-    },
-}
-
-#[derive(Serialize, Deserialize)]
-struct Tuple<T>(T,
-                #[serde(serialize_with = "ser_x", deserialize_with = "de_x")]
-                X);
-
-fn main() { }
diff --git a/src/test/pretty/auxiliary/derive-foo.rs b/src/test/pretty/auxiliary/derive-foo.rs
new file mode 100644
index 00000000000..bd81d3e5a3b
--- /dev/null
+++ b/src/test/pretty/auxiliary/derive-foo.rs
@@ -0,0 +1,22 @@
+// Copyright 2016 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.
+
+// no-prefer-dynamic
+
+#![crate_type = "proc-macro"]
+
+extern crate proc_macro;
+
+use proc_macro::TokenStream;
+
+#[proc_macro_derive(Foo, attributes(Bar))]
+pub fn derive(input: TokenStream) -> TokenStream {
+    "".parse().unwrap()
+}
diff --git a/src/test/ui/custom-derive/issue-36935.stderr b/src/test/ui/custom-derive/issue-36935.stderr
index 213366a307d..9a5e2de14e3 100644
--- a/src/test/ui/custom-derive/issue-36935.stderr
+++ b/src/test/ui/custom-derive/issue-36935.stderr
@@ -1,4 +1,4 @@
-error: custom derive attribute panicked
+error: proc-macro derive panicked
   --> $DIR/issue-36935.rs:17:15
    |
 17 | #[derive(Foo, Bar)]