about summary refs log tree commit diff
path: root/src/libsyntax
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2017-02-05 14:15:18 +0000
committerbors <bors@rust-lang.org>2017-02-05 14:15:18 +0000
commit9c8cdb2923a69177017165a4cdb0e1ea673fc49f (patch)
tree69ebb9fa31a84a98c42c14f56e866762a844ce2f /src/libsyntax
parent696f5c1fc695494053709ae3b18b4c6a65b619a6 (diff)
parent0a09274e27c7b409a321c0c7ed20d99492601b44 (diff)
downloadrust-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.rs24
-rw-r--r--src/libsyntax/ext/derive.rs218
-rw-r--r--src/libsyntax/ext/expand.rs114
-rw-r--r--src/libsyntax/feature_gate.rs4
-rw-r--r--src/libsyntax/lib.rs1
-rw-r--r--src/libsyntax/parse/parser.rs1
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.