diff options
| author | Jeffrey Seyfried <jeffrey.seyfried@gmail.com> | 2017-02-02 07:01:15 +0000 |
|---|---|---|
| committer | Jeffrey Seyfried <jeffrey.seyfried@gmail.com> | 2017-02-12 07:20:04 +0000 |
| commit | 2cc61eebb7f1677af2a20f76fb1411ed40f6901b (patch) | |
| tree | 499464b6464aef1624f01cf6c98c686ddbfa04f2 /src/libsyntax | |
| parent | 4b413bc393d8cde89ce0d687c3ef4ea50374af2c (diff) | |
| download | rust-2cc61eebb7f1677af2a20f76fb1411ed40f6901b.tar.gz rust-2cc61eebb7f1677af2a20f76fb1411ed40f6901b.zip | |
Allow using inert attributes from `proc_macro_derive`s with `#![feature(proc_macro)]`.
Diffstat (limited to 'src/libsyntax')
| -rw-r--r-- | src/libsyntax/ext/base.rs | 15 | ||||
| -rw-r--r-- | src/libsyntax/ext/derive.rs | 170 | ||||
| -rw-r--r-- | src/libsyntax/ext/expand.rs | 261 | ||||
| -rw-r--r-- | src/libsyntax/ext/placeholders.rs | 13 |
4 files changed, 231 insertions, 228 deletions
diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 9a717b86d09..b5afd0c453a 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -514,7 +514,7 @@ pub enum SyntaxExtension { /// The input is the annotated item. /// Allows generating code to implement a Trait for a given struct /// or enum item. - ProcMacroDerive(Box<MultiItemModifier>), + ProcMacroDerive(Box<MultiItemModifier>, Vec<Symbol> /* inert attribute names */), /// An attribute-like procedural macro that derives a builtin trait. BuiltinDerive(BuiltinDeriveFn), @@ -528,15 +528,15 @@ pub trait Resolver { fn eliminate_crate_var(&mut self, item: P<ast::Item>) -> P<ast::Item>; fn is_whitelisted_legacy_custom_derive(&self, name: Name) -> bool; - fn visit_expansion(&mut self, mark: Mark, expansion: &Expansion); + fn visit_expansion(&mut self, mark: Mark, expansion: &Expansion, derives: &[Mark]); fn add_ext(&mut self, ident: ast::Ident, ext: Rc<SyntaxExtension>); fn add_expansions_at_stmt(&mut self, id: ast::NodeId, macros: Vec<Mark>); fn resolve_imports(&mut self); - fn find_attr_invoc(&mut self, attrs: &mut Vec<Attribute>) -> Option<Attribute>; + // Resolves attribute and derive legacy macros from `#![plugin(..)]`. + fn find_legacy_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>; } @@ -555,19 +555,16 @@ impl Resolver for DummyResolver { fn eliminate_crate_var(&mut self, item: P<ast::Item>) -> P<ast::Item> { item } fn is_whitelisted_legacy_custom_derive(&self, _name: Name) -> bool { false } - fn visit_expansion(&mut self, _invoc: Mark, _expansion: &Expansion) {} + fn visit_expansion(&mut self, _invoc: Mark, _expansion: &Expansion, _derives: &[Mark]) {} fn add_ext(&mut self, _ident: ast::Ident, _ext: Rc<SyntaxExtension>) {} fn add_expansions_at_stmt(&mut self, _id: ast::NodeId, _macros: Vec<Mark>) {} fn resolve_imports(&mut self) {} - fn find_attr_invoc(&mut self, _attrs: &mut Vec<Attribute>) -> Option<Attribute> { None } + fn find_legacy_attr_invoc(&mut self, _attrs: &mut Vec<Attribute>) -> Option<Attribute> { None } fn resolve_macro(&mut self, _scope: Mark, _path: &ast::Path, _force: bool) -> 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) diff --git a/src/libsyntax/ext/derive.rs b/src/libsyntax/ext/derive.rs index ba14a153ed2..77cc7bab031 100644 --- a/src/libsyntax/ext/derive.rs +++ b/src/libsyntax/ext/derive.rs @@ -8,124 +8,42 @@ // 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 attr::HasAttrs; +use {ast, codemap}; +use ext::base::ExtCtxt; use ext::build::AstBuilder; 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 { +pub fn collect_derives(cx: &mut ExtCtxt, attrs: &mut Vec<ast::Attribute>) -> Vec<(Symbol, Span)> { + let mut result = Vec::new(); + attrs.retain(|attr| { if attr.name() != "derive" { - continue; + return true; } if attr.value_str().is_some() { cx.span_err(attr.span, "unexpected value in `derive`"); + return false; } 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; + return false; } + for titem in traits { if titem.word().is_none() { cx.span_err(titem.span, "malformed `derive` entry"); + return false; } - } - } -} - -#[derive(PartialEq, Debug, Clone, Copy)] -pub enum DeriveType { - ProcMacro, - Builtin -} - -impl DeriveType { - // Classify a derive trait name by resolving the macro. - pub fn classify(cx: &mut ExtCtxt, tname: Name) -> DeriveType { - 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; - } + result.push((titem.name().unwrap(), titem.span)); } - // 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; + true + }); + result } fn allow_unstable(cx: &mut ExtCtxt, span: Span, attr_name: &str) -> Span { @@ -142,43 +60,25 @@ fn allow_unstable(cx: &mut ExtCtxt, span: Span, attr_name: &str) -> 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::ProcMacro).or_else(|| { - add_derived_markers(cx, attrs); - get_derive_attr(cx, attrs, DeriveType::Builtin) +pub fn add_derived_markers<T: HasAttrs>(cx: &mut ExtCtxt, traits: &[(Symbol, Span)], item: T) -> T { + let span = match traits.get(0) { + Some(&(_, span)) => span, + None => return item, + }; + + item.map_attrs(|mut attrs| { + if traits.iter().any(|&(name, _)| name == "PartialEq") && + traits.iter().any(|&(name, _)| name == "Eq") { + let span = allow_unstable(cx, span, "derive(PartialEq, Eq)"); + let meta = cx.meta_word(span, Symbol::intern("structural_match")); + attrs.push(cx.attribute(span, meta)); + } + if traits.iter().any(|&(name, _)| name == "Copy") && + traits.iter().any(|&(name, _)| name == "Clone") { + let span = allow_unstable(cx, span, "derive(Copy, Clone)"); + let meta = cx.meta_word(span, Symbol::intern("rustc_copy_clone_marker")); + attrs.push(cx.attribute(span, meta)); + } + attrs }) } diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 8e7f8830eaf..d011d7c2a1c 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -14,10 +14,10 @@ use attr::{self, HasAttrs}; use codemap::{ExpnInfo, NameAndSpan, MacroBang, MacroAttribute}; use config::{is_test_or_bench, StripUnconfigured}; use ext::base::*; -use ext::derive::{find_derive_attr, derive_attr_trait}; +use ext::derive::{add_derived_markers, collect_derives}; use ext::hygiene::Mark; use ext::placeholders::{placeholder, PlaceholderExpander}; -use feature_gate::{self, Features}; +use feature_gate::{self, Features, is_builtin_attr}; use fold; use fold::*; use parse::parser::Parser; @@ -33,6 +33,7 @@ use tokenstream::{TokenTree, TokenStream}; use util::small_vector::SmallVector; use visit::Visitor; +use std::collections::HashMap; use std::mem; use std::path::PathBuf; use std::rc::Rc; @@ -164,11 +165,13 @@ pub enum InvocationKind { span: Span, }, Attr { - attr: ast::Attribute, + attr: Option<ast::Attribute>, + traits: Vec<(Symbol, Span)>, item: Annotatable, }, Derive { - attr: ast::Attribute, + name: Symbol, + span: Span, item: Annotatable, }, } @@ -177,8 +180,9 @@ impl Invocation { fn span(&self) -> Span { match self.kind { InvocationKind::Bang { span, .. } => span, - InvocationKind::Attr { ref attr, .. } => attr.span, - InvocationKind::Derive { ref attr, .. } => attr.span, + InvocationKind::Attr { attr: Some(ref attr), .. } => attr.span, + InvocationKind::Attr { attr: None, .. } => syntax_pos::DUMMY_SP, + InvocationKind::Derive { span, .. } => span, } } } @@ -227,15 +231,16 @@ impl<'a, 'b> MacroExpander<'a, 'b> { let orig_expansion_data = self.cx.current_expansion.clone(); self.cx.current_expansion.depth = 0; - let (expansion, mut invocations) = self.collect_invocations(expansion); + let (expansion, mut invocations) = self.collect_invocations(expansion, &[]); self.resolve_imports(); invocations.reverse(); let mut expansions = Vec::new(); + let mut derives = HashMap::new(); let mut undetermined_invocations = Vec::new(); let (mut progress, mut force) = (false, !self.monotonic); loop { - let invoc = if let Some(invoc) = invocations.pop() { + let mut invoc = if let Some(invoc) = invocations.pop() { invoc } else { self.resolve_imports(); @@ -247,24 +252,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { let scope = if self.monotonic { invoc.expansion_data.mark } else { orig_expansion_data.mark }; - let resolution = match invoc.kind { - InvocationKind::Bang { ref mac, .. } => { - self.cx.resolver.resolve_macro(scope, &mac.node.path, force) - } - InvocationKind::Attr { ref attr, .. } => { - let ident = Ident::with_empty_ctxt(attr.name()); - 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 { + let ext = match self.resolve_invoc(&mut invoc, scope, force) { Ok(ext) => Some(ext), Err(Determinacy::Determined) => None, Err(Determinacy::Undetermined) => { @@ -278,13 +266,49 @@ impl<'a, 'b> MacroExpander<'a, 'b> { self.cx.current_expansion = invoc.expansion_data.clone(); self.cx.current_expansion.mark = scope; - let expansion = match ext { - Some(ext) => self.expand_invoc(invoc, ext), - None => invoc.expansion_kind.dummy(invoc.span()), + // FIXME(jseyfried): Refactor out the following logic + let (expansion, new_invocations) = if let Some(ext) = ext { + if let Some(ext) = ext { + let expansion = self.expand_invoc(invoc, ext); + self.collect_invocations(expansion, &[]) + } else if let InvocationKind::Attr { attr: None, traits, item } = invoc.kind { + let item = item + .map_attrs(|mut attrs| { attrs.retain(|a| a.name() != "derive"); attrs }); + let item_with_markers = + add_derived_markers(&mut self.cx, &traits, item.clone()); + let derives = derives.entry(invoc.expansion_data.mark).or_insert_with(Vec::new); + + for &(name, span) in &traits { + let mark = Mark::fresh(); + derives.push(mark); + let path = ast::Path::from_ident(span, Ident::with_empty_ctxt(name)); + let item = match self.cx.resolver + .resolve_macro(Mark::root(), &path, false) { + Ok(ext) => match *ext { + SyntaxExtension::BuiltinDerive(..) => item_with_markers.clone(), + _ => item.clone(), + }, + _ => item.clone(), + }; + invocations.push(Invocation { + kind: InvocationKind::Derive { name: name, span: span, item: item }, + expansion_kind: invoc.expansion_kind, + expansion_data: ExpansionData { + mark: mark, + ..invoc.expansion_data.clone() + }, + }); + } + let expansion = invoc.expansion_kind + .expect_from_annotatables(::std::iter::once(item_with_markers)); + self.collect_invocations(expansion, derives) + } else { + unreachable!() + } + } else { + self.collect_invocations(invoc.expansion_kind.dummy(invoc.span()), &[]) }; - let (expansion, new_invocations) = self.collect_invocations(expansion); - if expansions.len() < depth { expansions.push(Vec::new()); } @@ -299,7 +323,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> { let mut placeholder_expander = PlaceholderExpander::new(self.cx, self.monotonic); while let Some(expansions) = expansions.pop() { for (mark, expansion) in expansions.into_iter().rev() { - placeholder_expander.add(mark.as_placeholder_id(), expansion); + let derives = derives.remove(&mark).unwrap_or_else(Vec::new); + placeholder_expander.add(mark.as_placeholder_id(), expansion, derives); } } @@ -314,7 +339,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } } - fn collect_invocations(&mut self, expansion: Expansion) -> (Expansion, Vec<Invocation>) { + fn collect_invocations(&mut self, expansion: Expansion, derives: &[Mark]) + -> (Expansion, Vec<Invocation>) { let result = { let mut collector = InvocationCollector { cfg: StripUnconfigured { @@ -332,13 +358,69 @@ impl<'a, 'b> MacroExpander<'a, 'b> { if self.monotonic { let err_count = self.cx.parse_sess.span_diagnostic.err_count(); let mark = self.cx.current_expansion.mark; - self.cx.resolver.visit_expansion(mark, &result.0); + self.cx.resolver.visit_expansion(mark, &result.0, derives); self.cx.resolve_err_count += self.cx.parse_sess.span_diagnostic.err_count() - err_count; } result } + fn resolve_invoc(&mut self, invoc: &mut Invocation, scope: Mark, force: bool) + -> Result<Option<Rc<SyntaxExtension>>, Determinacy> { + let (attr, traits, item) = match invoc.kind { + InvocationKind::Bang { ref mac, .. } => { + return self.cx.resolver.resolve_macro(scope, &mac.node.path, force).map(Some); + } + InvocationKind::Attr { attr: None, .. } => return Ok(None), + InvocationKind::Derive { name, span, .. } => { + let path = ast::Path::from_ident(span, Ident::with_empty_ctxt(name)); + return self.cx.resolver.resolve_derive_macro(scope, &path, force).map(Some); + } + InvocationKind::Attr { ref mut attr, ref traits, ref mut item } => (attr, traits, item), + }; + + let (attr_name, path) = { + let attr = attr.as_ref().unwrap(); + (attr.name(), ast::Path::from_ident(attr.span, Ident::with_empty_ctxt(attr.name()))) + }; + + let mut determined = true; + match self.cx.resolver.resolve_macro(scope, &path, force) { + Ok(ext) => return Ok(Some(ext)), + Err(Determinacy::Undetermined) => determined = false, + Err(Determinacy::Determined) if force => return Err(Determinacy::Determined), + _ => {} + } + + for &(name, span) in traits { + let path = ast::Path::from_ident(span, Ident::with_empty_ctxt(name)); + match self.cx.resolver.resolve_macro(scope, &path, force) { + Ok(ext) => if let SyntaxExtension::ProcMacroDerive(_, ref inert_attrs) = *ext { + if inert_attrs.contains(&attr_name) { + // FIXME(jseyfried) Avoid `mem::replace` here. + let dummy_item = placeholder(ExpansionKind::Items, ast::DUMMY_NODE_ID) + .make_items().pop().unwrap(); + *item = mem::replace(item, Annotatable::Item(dummy_item)) + .map_attrs(|mut attrs| { + let inert_attr = attr.take().unwrap(); + attr::mark_known(&inert_attr); + if self.cx.ecfg.proc_macro_enabled() { + *attr = find_attr_invoc(&mut attrs); + } + attrs.push(inert_attr); + attrs + }); + } + return Err(Determinacy::Undetermined); + }, + Err(Determinacy::Undetermined) => determined = false, + Err(Determinacy::Determined) => {} + } + } + + Err(if determined { Determinacy::Determined } else { Determinacy::Undetermined }) + } + fn expand_invoc(&mut self, invoc: Invocation, ext: Rc<SyntaxExtension>) -> Expansion { match invoc.kind { InvocationKind::Bang { .. } => self.expand_bang_invoc(invoc, ext), @@ -350,7 +432,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { fn expand_attr_invoc(&mut self, invoc: Invocation, ext: Rc<SyntaxExtension>) -> Expansion { let Invocation { expansion_kind: kind, .. } = invoc; let (attr, item) = match invoc.kind { - InvocationKind::Attr { attr, item } => (attr, item), + InvocationKind::Attr { attr, item, .. } => (attr.unwrap(), item), _ => unreachable!(), }; @@ -503,64 +585,60 @@ 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), + let (name, span, item) = match invoc.kind { + InvocationKind::Derive { name, span, item } => (name, span, 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; + let mitem = ast::MetaItem { name: name, span: span, node: ast::MetaItemKind::Word }; + let pretty_name = Symbol::intern(&format!("derive({})", name)); self.cx.bt_push(ExpnInfo { - call_site: attr.span, + call_site: span, callee: NameAndSpan { - format: MacroAttribute(attr.name()), - span: Some(attr.span), + format: MacroAttribute(pretty_name), + span: Some(span), allow_internal_unstable: false, } }); match *ext { - SyntaxExtension::ProcMacroDerive(ref ext) => { + SyntaxExtension::ProcMacroDerive(ref ext, _) => { let span = Span { expn_id: self.cx.codemap().record_expansion(ExpnInfo { - call_site: mitem.span, + call_site: span, callee: NameAndSpan { - format: MacroAttribute(Symbol::intern(&format!("derive({})", tname))), + format: MacroAttribute(pretty_name), span: None, allow_internal_unstable: false, }, }), - ..mitem.span + ..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, + call_site: span, callee: NameAndSpan { - format: MacroAttribute(name), + format: MacroAttribute(pretty_name), span: None, allow_internal_unstable: true, }, }), - ..titem.span + ..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) + self.cx.span_err(span, &msg); + kind.dummy(span) } } } @@ -672,34 +750,40 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { self.collect(kind, InvocationKind::Bang { mac: mac, ident: None, span: span }) } - fn collect_attr(&mut self, attr: ast::Attribute, item: Annotatable, kind: ExpansionKind) + fn collect_attr(&mut self, + attr: Option<ast::Attribute>, + traits: Vec<(Symbol, Span)>, + item: Annotatable, + kind: ExpansionKind) -> Expansion { - 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 !traits.is_empty() && + (kind == ExpansionKind::TraitItems || kind == ExpansionKind::ImplItems) { + self.cx.span_err(traits[0].1, "`derive` can be only be applied to items"); + return kind.expect_from_annotatables(::std::iter::once(item)); + } + self.collect(kind, InvocationKind::Attr { attr: attr, traits: traits, item: item }) } // 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; + fn classify_item<T>(&mut self, mut item: T) -> (Option<ast::Attribute>, Vec<(Symbol, Span)>, T) + where T: HasAttrs, + { + let (mut attr, mut traits) = (None, Vec::new()); item = item.map_attrs(|mut attrs| { - attr = self.cx.resolver.find_attr_invoc(&mut attrs).or_else(|| { - find_derive_attr(self.cx, &mut attrs) - }); + if let Some(legacy_attr_invoc) = self.cx.resolver.find_legacy_attr_invoc(&mut attrs) { + attr = Some(legacy_attr_invoc); + return attrs; + } + if self.cx.ecfg.proc_macro_enabled() { + attr = find_attr_invoc(&mut attrs); + } + traits = collect_derives(&mut self.cx, &mut attrs); attrs }); - (item, attr) + (attr, traits, item) } fn configure<T: HasAttrs>(&mut self, node: T) -> Option<T> { @@ -717,6 +801,16 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { } } +fn find_attr_invoc(attrs: &mut Vec<ast::Attribute>) -> Option<ast::Attribute> { + for i in 0 .. attrs.len() { + if !attr::is_known(&attrs[i]) && !is_builtin_attr(&attrs[i]) { + return Some(attrs.remove(i)); + } + } + + None +} + // These are pretty nasty. Ideally, we would keep the tokens around, linked from // the AST. However, we don't so we need to create new ones. Since the item might // have come from a macro expansion (possibly only in part), we can't use the @@ -844,10 +938,10 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { fn fold_item(&mut self, item: P<ast::Item>) -> SmallVector<P<ast::Item>> { let item = configure!(self, item); - let (mut item, attr) = self.classify_item(item); - if let Some(attr) = attr { + let (attr, traits, mut item) = self.classify_item(item); + if attr.is_some() || !traits.is_empty() { let item = Annotatable::Item(fully_configure!(self, item, noop_fold_item)); - return self.collect_attr(attr, item, ExpansionKind::Items).make_items(); + return self.collect_attr(attr, traits, item, ExpansionKind::Items).make_items(); } match item.node { @@ -928,11 +1022,12 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { fn fold_trait_item(&mut self, item: ast::TraitItem) -> SmallVector<ast::TraitItem> { let item = configure!(self, item); - let (item, attr) = self.classify_item(item); - if let Some(attr) = attr { + let (attr, traits, item) = self.classify_item(item); + if attr.is_some() || !traits.is_empty() { let item = Annotatable::TraitItem(P(fully_configure!(self, item, noop_fold_trait_item))); - return self.collect_attr(attr, item, ExpansionKind::TraitItems).make_trait_items() + return self.collect_attr(attr, traits, item, ExpansionKind::TraitItems) + .make_trait_items() } match item.node { @@ -948,10 +1043,11 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { fn fold_impl_item(&mut self, item: ast::ImplItem) -> SmallVector<ast::ImplItem> { let item = configure!(self, item); - let (item, attr) = self.classify_item(item); - if let Some(attr) = attr { + let (attr, traits, item) = self.classify_item(item); + if attr.is_some() || !traits.is_empty() { let item = Annotatable::ImplItem(P(fully_configure!(self, item, noop_fold_impl_item))); - return self.collect_attr(attr, item, ExpansionKind::ImplItems).make_impl_items(); + return self.collect_attr(attr, traits, item, ExpansionKind::ImplItems) + .make_impl_items(); } match item.node { @@ -1038,6 +1134,7 @@ impl<'feat> ExpansionConfig<'feat> { fn enable_trace_macros = trace_macros, fn enable_allow_internal_unstable = allow_internal_unstable, fn enable_custom_derive = custom_derive, + fn proc_macro_enabled = proc_macro, } } diff --git a/src/libsyntax/ext/placeholders.rs b/src/libsyntax/ext/placeholders.rs index 66555d7d95d..0636a78b215 100644 --- a/src/libsyntax/ext/placeholders.rs +++ b/src/libsyntax/ext/placeholders.rs @@ -84,8 +84,17 @@ impl<'a, 'b> PlaceholderExpander<'a, 'b> { } } - pub fn add(&mut self, id: ast::NodeId, expansion: Expansion) { - let expansion = expansion.fold_with(self); + pub fn add(&mut self, id: ast::NodeId, expansion: Expansion, derives: Vec<Mark>) { + let mut expansion = expansion.fold_with(self); + if let Expansion::Items(mut items) = expansion { + for derive in derives { + match self.remove(derive.as_placeholder_id()) { + Expansion::Items(derived_items) => items.extend(derived_items), + _ => unreachable!(), + } + } + expansion = Expansion::Items(items); + } self.expansions.insert(id, expansion); } |
