Move derive macro expansion into the MacroExpander
This removes the expand_derives function, and sprinkles the functionality throughout the Invocation Collector, Expander and Resolver.
This commit is contained in:
parent
0a7380d7fc
commit
fbdd038866
@ -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> {
|
||||
|
@ -536,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)]
|
||||
@ -562,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
218
src/libsyntax/ext/derive.rs
Normal 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)
|
||||
})
|
||||
}
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -54,7 +54,7 @@ impl MultiItemModifier for ProcMacroDerive {
|
||||
Annotatable::Item(item) => item,
|
||||
Annotatable::ImplItem(_) |
|
||||
Annotatable::TraitItem(_) => {
|
||||
ecx.span_err(span, "proc_macro_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 ProcMacroDerive {
|
||||
ItemKind::Struct(..) |
|
||||
ItemKind::Enum(..) => {},
|
||||
_ => {
|
||||
ecx.span_err(span, "proc_macro_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 ProcMacroDerive {
|
||||
let stream = match res {
|
||||
Ok(stream) => stream,
|
||||
Err(e) => {
|
||||
let msg = "proc_macro_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 ProcMacroDerive {
|
||||
Ok(new_items) => new_items,
|
||||
Err(_) => {
|
||||
// FIXME: handle this better
|
||||
let msg = "proc_macro_derive produced unparseable tokens";
|
||||
let msg = "proc-macro derive produced unparseable tokens";
|
||||
ecx.struct_span_fatal(span, msg).emit();
|
||||
panic!(FatalError);
|
||||
}
|
||||
|
@ -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,7 +36,7 @@ pub fn expand_deriving_decodable(cx: &mut ExtCtxt,
|
||||
mitem: &MetaItem,
|
||||
item: &Annotatable,
|
||||
push: &mut FnMut(Annotatable)) {
|
||||
deriving::warn_if_deprecated(cx, span, "Decodable");
|
||||
warn_if_deprecated(cx, span, "Decodable");
|
||||
expand_deriving_decodable_imp(cx, span, mitem, item, push, "serialize")
|
||||
}
|
||||
|
||||
|
@ -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,7 +113,7 @@ pub fn expand_deriving_encodable(cx: &mut ExtCtxt,
|
||||
mitem: &MetaItem,
|
||||
item: &Annotatable,
|
||||
push: &mut FnMut(Annotatable)) {
|
||||
deriving::warn_if_deprecated(cx, span, "Encodable");
|
||||
warn_if_deprecated(cx, span, "Encodable");
|
||||
expand_deriving_encodable_imp(cx, span, mitem, item, push, "serialize")
|
||||
}
|
||||
|
||||
|
@ -11,12 +11,10 @@
|
||||
//! The compiler code necessary to implement the `#[derive]` extensions.
|
||||
|
||||
use std::rc::Rc;
|
||||
use syntax::ast::{self, MetaItem};
|
||||
use syntax::attr::HasAttrs;
|
||||
use syntax::ast;
|
||||
use syntax::codemap;
|
||||
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;
|
||||
@ -90,241 +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::ProcMacroDerive(_) = *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::ProcMacroDerive(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 tname_cx = ast::Ident::with_empty_ctxt(titem.name().unwrap());
|
||||
let mitem = cx.meta_word(titem.span, name);
|
||||
let path = ast::Path::from_ident(titem.span, tname_cx);
|
||||
let ext = cx.resolver.resolve_macro(cx.current_expansion.mark, &path, false).unwrap();
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
if let SyntaxExtension::BuiltinDerive(ref func) = *ext {
|
||||
let my_item = Annotatable::Item(item);
|
||||
func(cx, span, &mitem, &my_item, &mut |a| {
|
||||
items.push(a)
|
||||
});
|
||||
item = my_item.expect_item();
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
|
@ -24,7 +24,6 @@
|
||||
#![feature(staged_api)]
|
||||
|
||||
extern crate fmt_macros;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
#[macro_use]
|
||||
extern crate syntax;
|
||||
@ -51,7 +50,7 @@ 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,
|
||||
@ -114,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);
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ extern crate derive_bad;
|
||||
#[derive(
|
||||
A
|
||||
)]
|
||||
//~^^ ERROR: proc_macro_derive produced unparseable tokens
|
||||
//~^^ ERROR: proc-macro derive produced unparseable tokens
|
||||
struct A;
|
||||
|
||||
fn main() {}
|
||||
|
@ -14,7 +14,7 @@
|
||||
extern crate derive_panic;
|
||||
|
||||
#[derive(A)]
|
||||
//~^ ERROR: proc_macro_derive attribute panicked
|
||||
//~^ ERROR: proc-macro derive panicked
|
||||
//~| HELP: message: nope!
|
||||
struct Foo;
|
||||
|
||||
|
@ -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() {}
|
||||
|
@ -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() {}
|
||||
|
@ -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!'
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -1,4 +1,4 @@
|
||||
error: proc_macro_derive attribute panicked
|
||||
error: proc-macro derive panicked
|
||||
--> $DIR/issue-36935.rs:17:15
|
||||
|
|
||||
17 | #[derive(Foo, Bar)]
|
||||
|
Loading…
Reference in New Issue
Block a user