about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2015-05-02 09:55:13 +0000
committerbors <bors@rust-lang.org>2015-05-02 09:55:13 +0000
commit354d16bd72ed002d6d8abe9896f89a445a60c21f (patch)
tree5054a0d9e455188615b17afd9b57f12342226202 /src
parent82158c9d1cc97d03b7e3a48fda0ff8c1e9209041 (diff)
parent5892b40859b65dd520ae92f7570069adaf313a8f (diff)
downloadrust-354d16bd72ed002d6d8abe9896f89a445a60c21f.tar.gz
rust-354d16bd72ed002d6d8abe9896f89a445a60c21f.zip
Auto merge of #25027 - Manishearth:deriving_attr, r=huonw
Adds an `attrs` field to `FieldInfo` which lets one check the attributes on
a field whilst expanding.

This lets deriving plugins be more robust, for example providing the ability to
"ignore" a field for the purpose of deriving, or perhaps handle the field a
different way.


r? @huonw
Diffstat (limited to 'src')
-rw-r--r--src/libsyntax/ext/build.rs4
-rw-r--r--src/libsyntax/ext/deriving/generic/mod.rs65
-rw-r--r--src/test/auxiliary/custom_derive_plugin.rs2
-rw-r--r--src/test/auxiliary/custom_derive_plugin_attr.rs87
-rw-r--r--src/test/run-pass-fulldeps/derive-totalsum-attr.rs74
5 files changed, 200 insertions, 32 deletions
diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs
index cde16d25412..354a0bff749 100644
--- a/src/libsyntax/ext/build.rs
+++ b/src/libsyntax/ext/build.rs
@@ -146,7 +146,7 @@ pub trait AstBuilder {
     fn expr_lit(&self, sp: Span, lit: ast::Lit_) -> P<ast::Expr>;
 
     fn expr_usize(&self, span: Span, i: usize) -> P<ast::Expr>;
-    fn expr_int(&self, sp: Span, i: isize) -> P<ast::Expr>;
+    fn expr_isize(&self, sp: Span, i: isize) -> P<ast::Expr>;
     fn expr_u8(&self, sp: Span, u: u8) -> P<ast::Expr>;
     fn expr_u32(&self, sp: Span, u: u32) -> P<ast::Expr>;
     fn expr_bool(&self, sp: Span, value: bool) -> P<ast::Expr>;
@@ -698,7 +698,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
     fn expr_usize(&self, span: Span, i: usize) -> P<ast::Expr> {
         self.expr_lit(span, ast::LitInt(i as u64, ast::UnsignedIntLit(ast::TyUs)))
     }
-    fn expr_int(&self, sp: Span, i: isize) -> P<ast::Expr> {
+    fn expr_isize(&self, sp: Span, i: isize) -> P<ast::Expr> {
         self.expr_lit(sp, ast::LitInt(i as u64, ast::SignedIntLit(ast::TyIs,
                                                                   ast::Sign::new(i))))
     }
diff --git a/src/libsyntax/ext/deriving/generic/mod.rs b/src/libsyntax/ext/deriving/generic/mod.rs
index 4c05cd973ff..339e535cdcd 100644
--- a/src/libsyntax/ext/deriving/generic/mod.rs
+++ b/src/libsyntax/ext/deriving/generic/mod.rs
@@ -270,7 +270,7 @@ pub struct Substructure<'a> {
 }
 
 /// Summary of the relevant parts of a struct/enum field.
-pub struct FieldInfo {
+pub struct FieldInfo<'a> {
     pub span: Span,
     /// None for tuple structs/normal enum variants, Some for normal
     /// structs/struct enum variants.
@@ -281,6 +281,8 @@ pub struct FieldInfo {
     /// The expressions corresponding to references to this field in
     /// the other `Self` arguments.
     pub other: Vec<P<Expr>>,
+    /// The attributes on the field
+    pub attrs: &'a [ast::Attribute],
 }
 
 /// Fields for a static method
@@ -293,11 +295,11 @@ pub enum StaticFields {
 
 /// A summary of the possible sets of fields.
 pub enum SubstructureFields<'a> {
-    Struct(Vec<FieldInfo>),
+    Struct(Vec<FieldInfo<'a>>),
     /// Matching variants of the enum: variant index, ast::Variant,
     /// fields: the field name is only non-`None` in the case of a struct
     /// variant.
-    EnumMatching(usize, &'a ast::Variant, Vec<FieldInfo>),
+    EnumMatching(usize, &'a ast::Variant, Vec<FieldInfo<'a>>),
 
     /// Non-matching variants of the enum, but with all state hidden from
     /// the consequent code.  The first component holds `Ident`s for all of
@@ -378,7 +380,7 @@ impl<'a> TraitDef<'a> {
     pub fn expand(&self,
                   cx: &mut ExtCtxt,
                   mitem: &ast::MetaItem,
-                  item: &ast::Item,
+                  item: &'a ast::Item,
                   push: &mut FnMut(P<ast::Item>))
     {
         let newitem = match item.node {
@@ -609,7 +611,7 @@ impl<'a> TraitDef<'a> {
 
     fn expand_struct_def(&self,
                          cx: &mut ExtCtxt,
-                         struct_def: &StructDef,
+                         struct_def: &'a StructDef,
                          type_ident: Ident,
                          generics: &Generics) -> P<ast::Item> {
         let field_tys: Vec<P<ast::Ty>> = struct_def.fields.iter()
@@ -653,7 +655,7 @@ impl<'a> TraitDef<'a> {
 
     fn expand_enum_def(&self,
                        cx: &mut ExtCtxt,
-                       enum_def: &EnumDef,
+                       enum_def: &'a EnumDef,
                        type_attrs: &[ast::Attribute],
                        type_ident: Ident,
                        generics: &Generics) -> P<ast::Item> {
@@ -885,10 +887,10 @@ impl<'a> MethodDef<'a> {
     ///     }
     /// }
     /// ```
-    fn expand_struct_method_body(&self,
+    fn expand_struct_method_body<'b>(&self,
                                  cx: &mut ExtCtxt,
-                                 trait_: &TraitDef,
-                                 struct_def: &StructDef,
+                                 trait_: &TraitDef<'b>,
+                                 struct_def: &'b StructDef,
                                  type_ident: Ident,
                                  self_args: &[P<Expr>],
                                  nonself_args: &[P<Expr>])
@@ -914,18 +916,19 @@ impl<'a> MethodDef<'a> {
         let fields = if !raw_fields.is_empty() {
             let mut raw_fields = raw_fields.into_iter().map(|v| v.into_iter());
             let first_field = raw_fields.next().unwrap();
-            let mut other_fields: Vec<vec::IntoIter<(Span, Option<Ident>, P<Expr>)>>
+            let mut other_fields: Vec<vec::IntoIter<_>>
                 = raw_fields.collect();
-            first_field.map(|(span, opt_id, field)| {
+            first_field.map(|(span, opt_id, field, attrs)| {
                 FieldInfo {
                     span: span,
                     name: opt_id,
                     self_: field,
                     other: other_fields.iter_mut().map(|l| {
                         match l.next().unwrap() {
-                            (_, _, ex) => ex
+                            (_, _, ex, _) => ex
                         }
-                    }).collect()
+                    }).collect(),
+                    attrs: attrs,
                 }
             }).collect()
         } else {
@@ -999,10 +1002,10 @@ impl<'a> MethodDef<'a> {
     /// `PartialEq`, and those subcomputations will hopefully be removed
     /// as their results are unused.  The point of `__self_vi` and
     /// `__arg_1_vi` is for `PartialOrd`; see #15503.)
-    fn expand_enum_method_body(&self,
+    fn expand_enum_method_body<'b>(&self,
                                cx: &mut ExtCtxt,
-                               trait_: &TraitDef,
-                               enum_def: &EnumDef,
+                               trait_: &TraitDef<'b>,
+                               enum_def: &'b EnumDef,
                                type_attrs: &[ast::Attribute],
                                type_ident: Ident,
                                self_args: Vec<P<Expr>>,
@@ -1038,11 +1041,11 @@ impl<'a> MethodDef<'a> {
     ///   }
     /// }
     /// ```
-    fn build_enum_match_tuple(
+    fn build_enum_match_tuple<'b>(
         &self,
         cx: &mut ExtCtxt,
-        trait_: &TraitDef,
-        enum_def: &EnumDef,
+        trait_: &TraitDef<'b>,
+        enum_def: &'b EnumDef,
         type_attrs: &[ast::Attribute],
         type_ident: Ident,
         self_args: Vec<P<Expr>>,
@@ -1125,7 +1128,7 @@ impl<'a> MethodDef<'a> {
                 // arg fields of the variant for the first self pat.
                 let field_tuples = first_self_pat_idents.into_iter().enumerate()
                     // For each arg field of self, pull out its getter expr ...
-                    .map(|(field_index, (sp, opt_ident, self_getter_expr))| {
+                    .map(|(field_index, (sp, opt_ident, self_getter_expr, attrs))| {
                         // ... but FieldInfo also wants getter expr
                         // for matching other arguments of Self type;
                         // so walk across the *other* self_pats_idents
@@ -1133,7 +1136,7 @@ impl<'a> MethodDef<'a> {
                         // of them (using `field_index` tracked above).
                         // That is the heart of the transposition.
                         let others = self_pats_idents.iter().map(|fields| {
-                            let (_, _opt_ident, ref other_getter_expr) =
+                            let (_, _opt_ident, ref other_getter_expr, _) =
                                 fields[field_index];
 
                             // All Self args have same variant, so
@@ -1149,6 +1152,7 @@ impl<'a> MethodDef<'a> {
                                     name: opt_ident,
                                     self_: self_getter_expr,
                                     other: others,
+                                    attrs: attrs,
                         }
                     }).collect::<Vec<FieldInfo>>();
 
@@ -1400,10 +1404,12 @@ impl<'a> TraitDef<'a> {
     fn create_struct_pattern(&self,
                              cx: &mut ExtCtxt,
                              struct_path: ast::Path,
-                             struct_def: &StructDef,
+                             struct_def: &'a StructDef,
                              prefix: &str,
                              mutbl: ast::Mutability)
-                             -> (P<ast::Pat>, Vec<(Span, Option<Ident>, P<Expr>)>) {
+                             -> (P<ast::Pat>, Vec<(Span, Option<Ident>,
+                                                   P<Expr>,
+                                                   &'a [ast::Attribute])>) {
         if struct_def.fields.is_empty() {
             return (cx.pat_enum(self.span, struct_path, vec![]), vec![]);
         }
@@ -1433,7 +1439,7 @@ impl<'a> TraitDef<'a> {
             paths.push(codemap::Spanned{span: sp, node: ident});
             let val = cx.expr(
                 sp, ast::ExprParen(cx.expr_deref(sp, cx.expr_path(cx.path_ident(sp,ident)))));
-            ident_expr.push((sp, opt_id, val));
+            ident_expr.push((sp, opt_id, val, &struct_field.node.attrs[..]));
         }
 
         let subpats = self.create_subpatterns(cx, paths, mutbl);
@@ -1441,7 +1447,8 @@ impl<'a> TraitDef<'a> {
         // struct_type is definitely not Unknown, since struct_def.fields
         // must be nonempty to reach here
         let pattern = if struct_type == Record {
-            let field_pats = subpats.into_iter().zip(ident_expr.iter()).map(|(pat, &(_, id, _))| {
+            let field_pats = subpats.into_iter().zip(ident_expr.iter())
+                                    .map(|(pat, &(_, id, _, _))| {
                 // id is guaranteed to be Some
                 codemap::Spanned {
                     span: pat.span,
@@ -1459,10 +1466,10 @@ impl<'a> TraitDef<'a> {
     fn create_enum_variant_pattern(&self,
                                    cx: &mut ExtCtxt,
                                    enum_ident: ast::Ident,
-                                   variant: &ast::Variant,
+                                   variant: &'a ast::Variant,
                                    prefix: &str,
                                    mutbl: ast::Mutability)
-        -> (P<ast::Pat>, Vec<(Span, Option<Ident>, P<Expr>)>) {
+        -> (P<ast::Pat>, Vec<(Span, Option<Ident>, P<Expr>, &'a [ast::Attribute])>) {
         let variant_ident = variant.node.name;
         let variant_path = cx.path(variant.span, vec![enum_ident, variant_ident]);
         match variant.node.kind {
@@ -1472,7 +1479,7 @@ impl<'a> TraitDef<'a> {
                 }
 
                 let mut paths = Vec::new();
-                let mut ident_expr = Vec::new();
+                let mut ident_expr: Vec<(_, _, _, &'a [ast::Attribute])> = Vec::new();
                 for (i, va) in variant_args.iter().enumerate() {
                     let sp = self.set_expn_info(cx, va.ty.span);
                     let ident = cx.ident_of(&format!("{}_{}", prefix, i));
@@ -1480,7 +1487,7 @@ impl<'a> TraitDef<'a> {
                     paths.push(path1);
                     let expr_path = cx.expr_path(cx.path_ident(sp, ident));
                     let val = cx.expr(sp, ast::ExprParen(cx.expr_deref(sp, expr_path)));
-                    ident_expr.push((sp, None, val));
+                    ident_expr.push((sp, None, val, &[]));
                 }
 
                 let subpats = self.create_subpatterns(cx, paths, mutbl);
diff --git a/src/test/auxiliary/custom_derive_plugin.rs b/src/test/auxiliary/custom_derive_plugin.rs
index 17d3f6a46ef..78381395dc9 100644
--- a/src/test/auxiliary/custom_derive_plugin.rs
+++ b/src/test/auxiliary/custom_derive_plugin.rs
@@ -55,7 +55,7 @@ fn expand(cx: &mut ExtCtxt,
                 ret_ty: Literal(Path::new_local("isize")),
                 attributes: vec![],
                 combine_substructure: combine_substructure(box |cx, span, substr| {
-                    let zero = cx.expr_int(span, 0);
+                    let zero = cx.expr_isize(span, 0);
                     cs_fold(false,
                             |cx, span, subexpr, field, _| {
                                 cx.expr_binary(span, ast::BiAdd, subexpr,
diff --git a/src/test/auxiliary/custom_derive_plugin_attr.rs b/src/test/auxiliary/custom_derive_plugin_attr.rs
new file mode 100644
index 00000000000..445aa743a77
--- /dev/null
+++ b/src/test/auxiliary/custom_derive_plugin_attr.rs
@@ -0,0 +1,87 @@
+// Copyright 2015 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.
+
+// force-host
+
+#![feature(plugin_registrar)]
+#![feature(box_syntax)]
+#![feature(rustc_private)]
+
+extern crate syntax;
+extern crate rustc;
+
+use syntax::ast;
+use syntax::attr::AttrMetaMethods;
+use syntax::codemap::Span;
+use syntax::ext::base::{Decorator, ExtCtxt};
+use syntax::ext::build::AstBuilder;
+use syntax::ext::deriving::generic::{cs_fold, TraitDef, MethodDef, combine_substructure};
+use syntax::ext::deriving::generic::{Substructure, Struct, EnumMatching};
+use syntax::ext::deriving::generic::ty::{Literal, LifetimeBounds, Path, borrowed_explicit_self};
+use syntax::parse::token;
+use syntax::ptr::P;
+use rustc::plugin::Registry;
+
+#[plugin_registrar]
+pub fn plugin_registrar(reg: &mut Registry) {
+    reg.register_syntax_extension(
+        token::intern("derive_TotalSum"),
+        Decorator(box expand));
+}
+
+fn expand(cx: &mut ExtCtxt,
+          span: Span,
+          mitem: &ast::MetaItem,
+          item: &ast::Item,
+          push: &mut FnMut(P<ast::Item>)) {
+    let trait_def = TraitDef {
+        span: span,
+        attributes: vec![],
+        path: Path::new(vec!["TotalSum"]),
+        additional_bounds: vec![],
+        generics: LifetimeBounds::empty(),
+        associated_types: vec![],
+        methods: vec![
+            MethodDef {
+                name: "total_sum",
+                generics: LifetimeBounds::empty(),
+                explicit_self: borrowed_explicit_self(),
+                args: vec![],
+                ret_ty: Literal(Path::new_local("isize")),
+                attributes: vec![],
+                combine_substructure: combine_substructure(Box::new(totalsum_substructure)),
+            },
+        ],
+    };
+
+    trait_def.expand(cx, mitem, item, push)
+}
+
+// Mostly copied from syntax::ext::deriving::hash
+/// Defines how the implementation for `trace()` is to be generated
+fn totalsum_substructure(cx: &mut ExtCtxt, trait_span: Span,
+                         substr: &Substructure) -> P<ast::Expr> {
+    let fields = match *substr.fields {
+        Struct(ref fs) | EnumMatching(_, _, ref fs) => fs,
+        _ => cx.span_bug(trait_span, "impossible substructure")
+    };
+
+    fields.iter().fold(cx.expr_isize(trait_span, 0), |acc, ref item| {
+        if item.attrs.iter().find(|a| a.check_name("ignore")).is_some() {
+            acc
+        } else {
+            cx.expr_binary(item.span, ast::BiAdd, acc,
+                           cx.expr_method_call(item.span,
+                                               item.self_.clone(),
+                                               substr.method_ident,
+                                               Vec::new()))
+        }
+    })
+}
diff --git a/src/test/run-pass-fulldeps/derive-totalsum-attr.rs b/src/test/run-pass-fulldeps/derive-totalsum-attr.rs
new file mode 100644
index 00000000000..ef5198b9ae0
--- /dev/null
+++ b/src/test/run-pass-fulldeps/derive-totalsum-attr.rs
@@ -0,0 +1,74 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// aux-build:custom_derive_plugin_attr.rs
+// ignore-stage1
+
+#![feature(plugin, custom_derive, custom_attribute)]
+#![plugin(custom_derive_plugin_attr)]
+
+trait TotalSum {
+    fn total_sum(&self) -> isize;
+}
+
+impl TotalSum for isize {
+    fn total_sum(&self) -> isize {
+        *self
+    }
+}
+
+struct Seven;
+
+impl TotalSum for Seven {
+    fn total_sum(&self) -> isize {
+        7
+    }
+}
+
+#[derive(TotalSum)]
+struct Foo {
+    seven: Seven,
+    bar: Bar,
+    baz: isize,
+    #[ignore]
+    nan: NaN,
+}
+
+#[derive(TotalSum)]
+struct Bar {
+    quux: isize,
+    bleh: isize,
+    #[ignore]
+    nan: NaN2
+}
+
+struct NaN;
+
+impl TotalSum for NaN {
+    fn total_sum(&self) -> isize {
+        panic!();
+    }
+}
+
+struct NaN2;
+
+pub fn main() {
+    let v = Foo {
+        seven: Seven,
+        bar: Bar {
+            quux: 9,
+            bleh: 3,
+            nan: NaN2
+        },
+        baz: 80,
+        nan: NaN
+    };
+    assert_eq!(v.total_sum(), 99);
+}