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
This commit is contained in:
Corey Farwell 2017-02-05 09:14:46 -05:00 committed by GitHub
commit 805a99e6cb
25 changed files with 483 additions and 342 deletions

View File

@ -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)));
}

View File

@ -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

View File

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

View File

@ -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)]

218
src/libsyntax/ext/derive.rs Normal file
View File

@ -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)
})
}

View File

@ -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)
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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")
}

View File

@ -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")
}

View File

@ -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))
);
)*
}
}
}

View File

@ -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);
}

View File

@ -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,

View File

@ -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() {}

View File

@ -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;

View File

@ -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

View File

@ -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() {}

View File

@ -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() {}

View File

@ -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!'
}

View File

@ -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

View File

@ -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`

View File

@ -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() { }

View File

@ -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() { }

View File

@ -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()
}

View File

@ -1,4 +1,4 @@
error: custom derive attribute panicked
error: proc-macro derive panicked
--> $DIR/issue-36935.rs:17:15
|
17 | #[derive(Foo, Bar)]