diff options
| author | bors <bors@rust-lang.org> | 2017-02-05 14:15:18 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2017-02-05 14:15:18 +0000 |
| commit | 9c8cdb2923a69177017165a4cdb0e1ea673fc49f (patch) | |
| tree | 69ebb9fa31a84a98c42c14f56e866762a844ce2f /src/libsyntax | |
| parent | 696f5c1fc695494053709ae3b18b4c6a65b619a6 (diff) | |
| parent | 0a09274e27c7b409a321c0c7ed20d99492601b44 (diff) | |
| download | rust-9c8cdb2923a69177017165a4cdb0e1ea673fc49f.tar.gz rust-9c8cdb2923a69177017165a4cdb0e1ea673fc49f.zip | |
Auto merge of #39563 - frewsxcv:rollup, r=frewsxcv
Rollup of 19 pull requests - Successful merges: #38518, #38921, #38959, #38983, #39009, #39107, #39193, #39289, #39312, #39393, #39442, #39443, #39453, #39454, #39471, #39477, #39478, #39527, #39552 - Failed merges:
Diffstat (limited to 'src/libsyntax')
| -rw-r--r-- | src/libsyntax/ext/base.rs | 24 | ||||
| -rw-r--r-- | src/libsyntax/ext/derive.rs | 218 | ||||
| -rw-r--r-- | src/libsyntax/ext/expand.rs | 114 | ||||
| -rw-r--r-- | src/libsyntax/feature_gate.rs | 4 | ||||
| -rw-r--r-- | src/libsyntax/lib.rs | 1 | ||||
| -rw-r--r-- | src/libsyntax/parse/parser.rs | 1 |
6 files changed, 348 insertions, 14 deletions
diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index edf74e1fe19..9a717b86d09 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -10,7 +10,7 @@ pub use self::SyntaxExtension::{MultiDecorator, MultiModifier, NormalTT, IdentTT}; -use ast::{self, Attribute, Name, PatKind}; +use ast::{self, Attribute, Name, PatKind, MetaItem}; use attr::HasAttrs; use codemap::{self, CodeMap, ExpnInfo, Spanned, respan}; use syntax_pos::{Span, ExpnId, NO_EXPANSION}; @@ -471,6 +471,9 @@ impl MacResult for DummyResult { } } +pub type BuiltinDeriveFn = + for<'cx> fn(&'cx mut ExtCtxt, Span, &MetaItem, &Annotatable, &mut FnMut(Annotatable)); + /// An enum representing the different kinds of syntax extensions. pub enum SyntaxExtension { /// A syntax extension that is attached to an item and creates new items @@ -507,7 +510,14 @@ pub enum SyntaxExtension { /// IdentTT(Box<IdentMacroExpander>, Option<Span>, bool), - CustomDerive(Box<MultiItemModifier>), + /// An attribute-like procedural macro. TokenStream -> TokenStream. + /// The input is the annotated item. + /// Allows generating code to implement a Trait for a given struct + /// or enum item. + ProcMacroDerive(Box<MultiItemModifier>), + + /// An attribute-like procedural macro that derives a builtin trait. + BuiltinDerive(BuiltinDeriveFn), } pub type NamedSyntaxExtension = (Name, SyntaxExtension); @@ -526,6 +536,9 @@ pub trait Resolver { fn find_attr_invoc(&mut self, attrs: &mut Vec<Attribute>) -> Option<Attribute>; fn resolve_macro(&mut self, scope: Mark, path: &ast::Path, force: bool) -> Result<Rc<SyntaxExtension>, Determinacy>; + fn resolve_builtin_macro(&mut self, tname: Name) -> Result<Rc<SyntaxExtension>, Determinacy>; + fn resolve_derive_macro(&mut self, scope: Mark, path: &ast::Path, force: bool) + -> Result<Rc<SyntaxExtension>, Determinacy>; } #[derive(Copy, Clone, Debug)] @@ -552,6 +565,13 @@ impl Resolver for DummyResolver { -> Result<Rc<SyntaxExtension>, Determinacy> { Err(Determinacy::Determined) } + fn resolve_builtin_macro(&mut self, _tname: Name) -> Result<Rc<SyntaxExtension>, Determinacy> { + Err(Determinacy::Determined) + } + fn resolve_derive_macro(&mut self, _scope: Mark, _path: &ast::Path, _force: bool) + -> Result<Rc<SyntaxExtension>, Determinacy> { + Err(Determinacy::Determined) + } } #[derive(Clone)] diff --git a/src/libsyntax/ext/derive.rs b/src/libsyntax/ext/derive.rs new file mode 100644 index 00000000000..946448eaaee --- /dev/null +++ b/src/libsyntax/ext/derive.rs @@ -0,0 +1,218 @@ +// Copyright 2012-2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use ast::Name; +use attr; +use ast::{self, NestedMetaItem}; use ext::base::{ExtCtxt, SyntaxExtension}; +use codemap; +use ext::build::AstBuilder; +use feature_gate; +use symbol::Symbol; +use syntax_pos::Span; + +pub fn derive_attr_trait<'a>(cx: &mut ExtCtxt, attr: &'a ast::Attribute) + -> Option<&'a NestedMetaItem> { + if attr.name() != "derive" { + return None; + } + if attr.value_str().is_some() { + cx.span_err(attr.span, "unexpected value in `derive`"); + return None; + } + + let traits = attr.meta_item_list().unwrap_or(&[]); + + if traits.is_empty() { + cx.span_warn(attr.span, "empty trait list in `derive`"); + return None; + } + + return traits.get(0); +} + +pub fn verify_derive_attrs(cx: &mut ExtCtxt, attrs: &[ast::Attribute]) { + for attr in attrs { + if attr.name() != "derive" { + continue; + } + + if attr.value_str().is_some() { + cx.span_err(attr.span, "unexpected value in `derive`"); + } + + let traits = attr.meta_item_list().unwrap_or(&[]).to_owned(); + + if traits.is_empty() { + cx.span_warn(attr.span, "empty trait list in `derive`"); + attr::mark_used(&attr); + continue; + } + for titem in traits { + if titem.word().is_none() { + cx.span_err(titem.span, "malformed `derive` entry"); + } + } + } +} + +#[derive(PartialEq, Debug, Clone, Copy)] +pub enum DeriveType { + Legacy, + ProcMacro, + Builtin +} + +impl DeriveType { + // Classify a derive trait name by resolving the macro. + pub fn classify(cx: &mut ExtCtxt, tname: Name) -> DeriveType { + let legacy_derive_name = Symbol::intern(&format!("derive_{}", tname)); + + if let Ok(_) = cx.resolver.resolve_builtin_macro(legacy_derive_name) { + return DeriveType::Legacy; + } + + match cx.resolver.resolve_builtin_macro(tname) { + Ok(ext) => match *ext { + SyntaxExtension::BuiltinDerive(..) => DeriveType::Builtin, + _ => DeriveType::ProcMacro, + }, + Err(_) => DeriveType::ProcMacro, + } + } +} + +pub fn get_derive_attr(cx: &mut ExtCtxt, attrs: &mut Vec<ast::Attribute>, + derive_type: DeriveType) -> Option<ast::Attribute> { + for i in 0..attrs.len() { + if attrs[i].name() != "derive" { + continue; + } + + if attrs[i].value_str().is_some() { + continue; + } + + let mut traits = attrs[i].meta_item_list().unwrap_or(&[]).to_owned(); + + // First, weed out malformed #[derive] + traits.retain(|titem| titem.word().is_some()); + + let mut titem = None; + + // See if we can find a matching trait. + for j in 0..traits.len() { + let tname = match traits[j].name() { + Some(tname) => tname, + _ => continue, + }; + + if DeriveType::classify(cx, tname) == derive_type { + titem = Some(traits.remove(j)); + break; + } + } + + // If we find a trait, remove the trait from the attribute. + if let Some(titem) = titem { + if traits.len() == 0 { + attrs.remove(i); + } else { + let derive = Symbol::intern("derive"); + let mitem = cx.meta_list(titem.span, derive, traits); + attrs[i] = cx.attribute(titem.span, mitem); + } + let derive = Symbol::intern("derive"); + let mitem = cx.meta_list(titem.span, derive, vec![titem]); + return Some(cx.attribute(mitem.span, mitem)); + } + } + return None; +} + +fn allow_unstable(cx: &mut ExtCtxt, span: Span, attr_name: &str) -> Span { + Span { + expn_id: cx.codemap().record_expansion(codemap::ExpnInfo { + call_site: span, + callee: codemap::NameAndSpan { + format: codemap::MacroAttribute(Symbol::intern(attr_name)), + span: Some(span), + allow_internal_unstable: true, + }, + }), + ..span + } +} + +pub fn add_derived_markers(cx: &mut ExtCtxt, attrs: &mut Vec<ast::Attribute>) { + if attrs.is_empty() { + return; + } + + let titems = attrs.iter().filter(|a| { + a.name() == "derive" + }).flat_map(|a| { + a.meta_item_list().unwrap_or(&[]).iter() + }).filter_map(|titem| { + titem.name() + }).collect::<Vec<_>>(); + + let span = attrs[0].span; + + if !attrs.iter().any(|a| a.name() == "structural_match") && + titems.iter().any(|t| *t == "PartialEq") && titems.iter().any(|t| *t == "Eq") { + let structural_match = Symbol::intern("structural_match"); + let span = allow_unstable(cx, span, "derive(PartialEq, Eq)"); + let meta = cx.meta_word(span, structural_match); + attrs.push(cx.attribute(span, meta)); + } + + if !attrs.iter().any(|a| a.name() == "rustc_copy_clone_marker") && + titems.iter().any(|t| *t == "Copy") && titems.iter().any(|t| *t == "Clone") { + let structural_match = Symbol::intern("rustc_copy_clone_marker"); + let span = allow_unstable(cx, span, "derive(Copy, Clone)"); + let meta = cx.meta_word(span, structural_match); + attrs.push(cx.attribute(span, meta)); + } +} + +pub fn find_derive_attr(cx: &mut ExtCtxt, attrs: &mut Vec<ast::Attribute>) + -> Option<ast::Attribute> { + verify_derive_attrs(cx, attrs); + get_derive_attr(cx, attrs, DeriveType::Legacy).and_then(|a| { + let titem = derive_attr_trait(cx, &a); + titem.and_then(|titem| { + let tword = titem.word().unwrap(); + let tname = tword.name(); + if !cx.ecfg.enable_custom_derive() { + feature_gate::emit_feature_err( + &cx.parse_sess, + "custom_derive", + titem.span, + feature_gate::GateIssue::Language, + feature_gate::EXPLAIN_CUSTOM_DERIVE + ); + None + } else { + let name = Symbol::intern(&format!("derive_{}", tname)); + if !cx.resolver.is_whitelisted_legacy_custom_derive(name) { + cx.span_warn(titem.span, + feature_gate::EXPLAIN_DEPR_CUSTOM_DERIVE); + } + let mitem = cx.meta_word(titem.span, name); + Some(cx.attribute(mitem.span, mitem)) + } + }) + }).or_else(|| { + get_derive_attr(cx, attrs, DeriveType::ProcMacro) + }).or_else(|| { + add_derived_markers(cx, attrs); + get_derive_attr(cx, attrs, DeriveType::Builtin) + }) +} diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 226625ebc8e..8e7f8830eaf 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -8,26 +8,27 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use ast::{Block, Ident, Mac_, PatKind}; +use ast::{self, Block, Ident, Mac_, PatKind}; use ast::{Name, MacStmtStyle, StmtKind, ItemKind}; -use ast; -use ext::hygiene::Mark; -use ext::placeholders::{placeholder, PlaceholderExpander}; use attr::{self, HasAttrs}; use codemap::{ExpnInfo, NameAndSpan, MacroBang, MacroAttribute}; -use syntax_pos::{self, Span, ExpnId}; use config::{is_test_or_bench, StripUnconfigured}; use ext::base::*; +use ext::derive::{find_derive_attr, derive_attr_trait}; +use ext::hygiene::Mark; +use ext::placeholders::{placeholder, PlaceholderExpander}; use feature_gate::{self, Features}; use fold; use fold::*; -use parse::{ParseSess, DirectoryOwnership, PResult, filemap_to_tts}; use parse::parser::Parser; use parse::token; +use parse::{ParseSess, DirectoryOwnership, PResult, filemap_to_tts}; use print::pprust; use ptr::P; use std_inject; +use symbol::Symbol; use symbol::keywords; +use syntax_pos::{self, Span, ExpnId}; use tokenstream::{TokenTree, TokenStream}; use util::small_vector::SmallVector; use visit::Visitor; @@ -166,6 +167,10 @@ pub enum InvocationKind { attr: ast::Attribute, item: Annotatable, }, + Derive { + attr: ast::Attribute, + item: Annotatable, + }, } impl Invocation { @@ -173,6 +178,7 @@ impl Invocation { match self.kind { InvocationKind::Bang { span, .. } => span, InvocationKind::Attr { ref attr, .. } => attr.span, + InvocationKind::Derive { ref attr, .. } => attr.span, } } } @@ -250,6 +256,13 @@ impl<'a, 'b> MacroExpander<'a, 'b> { let path = ast::Path::from_ident(attr.span, ident); self.cx.resolver.resolve_macro(scope, &path, force) } + InvocationKind::Derive { ref attr, .. } => { + let titem = derive_attr_trait(self.cx, &attr).unwrap(); + let tname = titem.name().expect("Expected derive macro name"); + let ident = Ident::with_empty_ctxt(tname); + let path = ast::Path::from_ident(attr.span, ident); + self.cx.resolver.resolve_derive_macro(scope, &path, force) + } }; let ext = match resolution { Ok(ext) => Some(ext), @@ -330,6 +343,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { match invoc.kind { InvocationKind::Bang { .. } => self.expand_bang_invoc(invoc, ext), InvocationKind::Attr { .. } => self.expand_attr_invoc(invoc, ext), + InvocationKind::Derive { .. } => self.expand_derive_invoc(invoc, ext), } } @@ -370,7 +384,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { let tok_result = mac.expand(self.cx, attr.span, attr_toks, item_toks); self.parse_expansion(tok_result, kind, name, attr.span) } - SyntaxExtension::CustomDerive(_) => { + SyntaxExtension::ProcMacroDerive(..) | SyntaxExtension::BuiltinDerive(..) => { self.cx.span_err(attr.span, &format!("`{}` is a derive mode", name)); kind.dummy(attr.span) } @@ -440,7 +454,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { return kind.dummy(span); } - SyntaxExtension::CustomDerive(..) => { + SyntaxExtension::ProcMacroDerive(..) | SyntaxExtension::BuiltinDerive(..) => { self.cx.span_err(path.span, &format!("`{}` is a derive mode", extname)); return kind.dummy(span); } @@ -486,6 +500,71 @@ impl<'a, 'b> MacroExpander<'a, 'b> { }) } + /// Expand a derive invocation. Returns the result of expansion. + fn expand_derive_invoc(&mut self, invoc: Invocation, ext: Rc<SyntaxExtension>) -> Expansion { + let Invocation { expansion_kind: kind, .. } = invoc; + let (attr, item) = match invoc.kind { + InvocationKind::Derive { attr, item } => (attr, item), + _ => unreachable!(), + }; + + attr::mark_used(&attr); + let titem = derive_attr_trait(self.cx, &attr).unwrap(); + let tname = ast::Ident::with_empty_ctxt(titem.name().unwrap()); + let name = Symbol::intern(&format!("derive({})", tname)); + let mitem = &attr.value; + + self.cx.bt_push(ExpnInfo { + call_site: attr.span, + callee: NameAndSpan { + format: MacroAttribute(attr.name()), + span: Some(attr.span), + allow_internal_unstable: false, + } + }); + + match *ext { + SyntaxExtension::ProcMacroDerive(ref ext) => { + let span = Span { + expn_id: self.cx.codemap().record_expansion(ExpnInfo { + call_site: mitem.span, + callee: NameAndSpan { + format: MacroAttribute(Symbol::intern(&format!("derive({})", tname))), + span: None, + allow_internal_unstable: false, + }, + }), + ..mitem.span + }; + return kind.expect_from_annotatables(ext.expand(self.cx, span, &mitem, item)); + } + SyntaxExtension::BuiltinDerive(func) => { + let span = Span { + expn_id: self.cx.codemap().record_expansion(ExpnInfo { + call_site: titem.span, + callee: NameAndSpan { + format: MacroAttribute(name), + span: None, + allow_internal_unstable: true, + }, + }), + ..titem.span + }; + let mut items = Vec::new(); + func(self.cx, span, &mitem, &item, &mut |a| { + items.push(a) + }); + items.insert(0, item); + return kind.expect_from_annotatables(items); + } + _ => { + let msg = &format!("macro `{}` may not be used for derive attributes", name); + self.cx.span_err(attr.span, &msg); + kind.dummy(attr.span) + } + } + } + fn parse_expansion(&mut self, toks: TokenStream, kind: ExpansionKind, name: Name, span: Span) -> Expansion { let mut parser = self.cx.new_parser_from_tts(&toks.trees().cloned().collect::<Vec<_>>()); @@ -595,16 +674,31 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { fn collect_attr(&mut self, attr: ast::Attribute, item: Annotatable, kind: ExpansionKind) -> Expansion { - self.collect(kind, InvocationKind::Attr { attr: attr, item: item }) + let invoc_kind = if attr.name() == "derive" { + if kind == ExpansionKind::TraitItems || kind == ExpansionKind::ImplItems { + self.cx.span_err(attr.span, "`derive` can be only be applied to items"); + return kind.expect_from_annotatables(::std::iter::once(item)); + } + InvocationKind::Derive { attr: attr, item: item } + } else { + InvocationKind::Attr { attr: attr, item: item } + }; + + self.collect(kind, invoc_kind) } // If `item` is an attr invocation, remove and return the macro attribute. fn classify_item<T: HasAttrs>(&mut self, mut item: T) -> (T, Option<ast::Attribute>) { let mut attr = None; + item = item.map_attrs(|mut attrs| { - attr = self.cx.resolver.find_attr_invoc(&mut attrs); + attr = self.cx.resolver.find_attr_invoc(&mut attrs).or_else(|| { + find_derive_attr(self.cx, &mut attrs) + }); + attrs }); + (item, attr) } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 970c37045df..52ef2a05fcf 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -318,7 +318,7 @@ declare_features! ( (active, abi_unadjusted, "1.16.0", None), // Macros 1.1 - (active, proc_macro, "1.16.0", Some(35900)), + (active, proc_macro, "1.16.0", Some(38356)), // Allows attributes on struct literal fields. (active, struct_field_attributes, "1.16.0", Some(38814)), @@ -381,7 +381,7 @@ declare_features! ( (accepted, dotdot_in_tuple_patterns, "1.14.0", Some(33627)), (accepted, item_like_imports, "1.14.0", Some(35120)), // Allows using `Self` and associated types in struct expressions and patterns. - (accepted, more_struct_aliases, "1.14.0", Some(37544)), + (accepted, more_struct_aliases, "1.16.0", Some(37544)), ); // (changing above list without updating src/doc/reference.md makes @cmr sad) diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs index 25be9e91a6a..87a03adf6b7 100644 --- a/src/libsyntax/lib.rs +++ b/src/libsyntax/lib.rs @@ -128,6 +128,7 @@ pub mod print { pub mod ext { pub mod base; pub mod build; + pub mod derive; pub mod expand; pub mod placeholders; pub mod hygiene; diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index fd7c56f136f..45d8354d317 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -1691,6 +1691,7 @@ impl<'a> Parser<'a> { } // Assemble the span. + // FIXME(#39450) This is bogus if part of the path is macro generated. let span = mk_sp(lo, self.prev_span.hi); // Assemble the result. |
