Skip to content

Commit 354d16b

Browse files
committed
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
2 parents 82158c9 + 5892b40 commit 354d16b

File tree

5 files changed

+200
-32
lines changed

5 files changed

+200
-32
lines changed

src/libsyntax/ext/build.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ pub trait AstBuilder {
146146
fn expr_lit(&self, sp: Span, lit: ast::Lit_) -> P<ast::Expr>;
147147

148148
fn expr_usize(&self, span: Span, i: usize) -> P<ast::Expr>;
149-
fn expr_int(&self, sp: Span, i: isize) -> P<ast::Expr>;
149+
fn expr_isize(&self, sp: Span, i: isize) -> P<ast::Expr>;
150150
fn expr_u8(&self, sp: Span, u: u8) -> P<ast::Expr>;
151151
fn expr_u32(&self, sp: Span, u: u32) -> P<ast::Expr>;
152152
fn expr_bool(&self, sp: Span, value: bool) -> P<ast::Expr>;
@@ -698,7 +698,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
698698
fn expr_usize(&self, span: Span, i: usize) -> P<ast::Expr> {
699699
self.expr_lit(span, ast::LitInt(i as u64, ast::UnsignedIntLit(ast::TyUs)))
700700
}
701-
fn expr_int(&self, sp: Span, i: isize) -> P<ast::Expr> {
701+
fn expr_isize(&self, sp: Span, i: isize) -> P<ast::Expr> {
702702
self.expr_lit(sp, ast::LitInt(i as u64, ast::SignedIntLit(ast::TyIs,
703703
ast::Sign::new(i))))
704704
}

src/libsyntax/ext/deriving/generic/mod.rs

+36-29
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ pub struct Substructure<'a> {
270270
}
271271

272272
/// Summary of the relevant parts of a struct/enum field.
273-
pub struct FieldInfo {
273+
pub struct FieldInfo<'a> {
274274
pub span: Span,
275275
/// None for tuple structs/normal enum variants, Some for normal
276276
/// structs/struct enum variants.
@@ -281,6 +281,8 @@ pub struct FieldInfo {
281281
/// The expressions corresponding to references to this field in
282282
/// the other `Self` arguments.
283283
pub other: Vec<P<Expr>>,
284+
/// The attributes on the field
285+
pub attrs: &'a [ast::Attribute],
284286
}
285287

286288
/// Fields for a static method
@@ -293,11 +295,11 @@ pub enum StaticFields {
293295

294296
/// A summary of the possible sets of fields.
295297
pub enum SubstructureFields<'a> {
296-
Struct(Vec<FieldInfo>),
298+
Struct(Vec<FieldInfo<'a>>),
297299
/// Matching variants of the enum: variant index, ast::Variant,
298300
/// fields: the field name is only non-`None` in the case of a struct
299301
/// variant.
300-
EnumMatching(usize, &'a ast::Variant, Vec<FieldInfo>),
302+
EnumMatching(usize, &'a ast::Variant, Vec<FieldInfo<'a>>),
301303

302304
/// Non-matching variants of the enum, but with all state hidden from
303305
/// the consequent code. The first component holds `Ident`s for all of
@@ -378,7 +380,7 @@ impl<'a> TraitDef<'a> {
378380
pub fn expand(&self,
379381
cx: &mut ExtCtxt,
380382
mitem: &ast::MetaItem,
381-
item: &ast::Item,
383+
item: &'a ast::Item,
382384
push: &mut FnMut(P<ast::Item>))
383385
{
384386
let newitem = match item.node {
@@ -609,7 +611,7 @@ impl<'a> TraitDef<'a> {
609611

610612
fn expand_struct_def(&self,
611613
cx: &mut ExtCtxt,
612-
struct_def: &StructDef,
614+
struct_def: &'a StructDef,
613615
type_ident: Ident,
614616
generics: &Generics) -> P<ast::Item> {
615617
let field_tys: Vec<P<ast::Ty>> = struct_def.fields.iter()
@@ -653,7 +655,7 @@ impl<'a> TraitDef<'a> {
653655

654656
fn expand_enum_def(&self,
655657
cx: &mut ExtCtxt,
656-
enum_def: &EnumDef,
658+
enum_def: &'a EnumDef,
657659
type_attrs: &[ast::Attribute],
658660
type_ident: Ident,
659661
generics: &Generics) -> P<ast::Item> {
@@ -885,10 +887,10 @@ impl<'a> MethodDef<'a> {
885887
/// }
886888
/// }
887889
/// ```
888-
fn expand_struct_method_body(&self,
890+
fn expand_struct_method_body<'b>(&self,
889891
cx: &mut ExtCtxt,
890-
trait_: &TraitDef,
891-
struct_def: &StructDef,
892+
trait_: &TraitDef<'b>,
893+
struct_def: &'b StructDef,
892894
type_ident: Ident,
893895
self_args: &[P<Expr>],
894896
nonself_args: &[P<Expr>])
@@ -914,18 +916,19 @@ impl<'a> MethodDef<'a> {
914916
let fields = if !raw_fields.is_empty() {
915917
let mut raw_fields = raw_fields.into_iter().map(|v| v.into_iter());
916918
let first_field = raw_fields.next().unwrap();
917-
let mut other_fields: Vec<vec::IntoIter<(Span, Option<Ident>, P<Expr>)>>
919+
let mut other_fields: Vec<vec::IntoIter<_>>
918920
= raw_fields.collect();
919-
first_field.map(|(span, opt_id, field)| {
921+
first_field.map(|(span, opt_id, field, attrs)| {
920922
FieldInfo {
921923
span: span,
922924
name: opt_id,
923925
self_: field,
924926
other: other_fields.iter_mut().map(|l| {
925927
match l.next().unwrap() {
926-
(_, _, ex) => ex
928+
(_, _, ex, _) => ex
927929
}
928-
}).collect()
930+
}).collect(),
931+
attrs: attrs,
929932
}
930933
}).collect()
931934
} else {
@@ -999,10 +1002,10 @@ impl<'a> MethodDef<'a> {
9991002
/// `PartialEq`, and those subcomputations will hopefully be removed
10001003
/// as their results are unused. The point of `__self_vi` and
10011004
/// `__arg_1_vi` is for `PartialOrd`; see #15503.)
1002-
fn expand_enum_method_body(&self,
1005+
fn expand_enum_method_body<'b>(&self,
10031006
cx: &mut ExtCtxt,
1004-
trait_: &TraitDef,
1005-
enum_def: &EnumDef,
1007+
trait_: &TraitDef<'b>,
1008+
enum_def: &'b EnumDef,
10061009
type_attrs: &[ast::Attribute],
10071010
type_ident: Ident,
10081011
self_args: Vec<P<Expr>>,
@@ -1038,11 +1041,11 @@ impl<'a> MethodDef<'a> {
10381041
/// }
10391042
/// }
10401043
/// ```
1041-
fn build_enum_match_tuple(
1044+
fn build_enum_match_tuple<'b>(
10421045
&self,
10431046
cx: &mut ExtCtxt,
1044-
trait_: &TraitDef,
1045-
enum_def: &EnumDef,
1047+
trait_: &TraitDef<'b>,
1048+
enum_def: &'b EnumDef,
10461049
type_attrs: &[ast::Attribute],
10471050
type_ident: Ident,
10481051
self_args: Vec<P<Expr>>,
@@ -1125,15 +1128,15 @@ impl<'a> MethodDef<'a> {
11251128
// arg fields of the variant for the first self pat.
11261129
let field_tuples = first_self_pat_idents.into_iter().enumerate()
11271130
// For each arg field of self, pull out its getter expr ...
1128-
.map(|(field_index, (sp, opt_ident, self_getter_expr))| {
1131+
.map(|(field_index, (sp, opt_ident, self_getter_expr, attrs))| {
11291132
// ... but FieldInfo also wants getter expr
11301133
// for matching other arguments of Self type;
11311134
// so walk across the *other* self_pats_idents
11321135
// and pull out getter for same field in each
11331136
// of them (using `field_index` tracked above).
11341137
// That is the heart of the transposition.
11351138
let others = self_pats_idents.iter().map(|fields| {
1136-
let (_, _opt_ident, ref other_getter_expr) =
1139+
let (_, _opt_ident, ref other_getter_expr, _) =
11371140
fields[field_index];
11381141

11391142
// All Self args have same variant, so
@@ -1149,6 +1152,7 @@ impl<'a> MethodDef<'a> {
11491152
name: opt_ident,
11501153
self_: self_getter_expr,
11511154
other: others,
1155+
attrs: attrs,
11521156
}
11531157
}).collect::<Vec<FieldInfo>>();
11541158

@@ -1400,10 +1404,12 @@ impl<'a> TraitDef<'a> {
14001404
fn create_struct_pattern(&self,
14011405
cx: &mut ExtCtxt,
14021406
struct_path: ast::Path,
1403-
struct_def: &StructDef,
1407+
struct_def: &'a StructDef,
14041408
prefix: &str,
14051409
mutbl: ast::Mutability)
1406-
-> (P<ast::Pat>, Vec<(Span, Option<Ident>, P<Expr>)>) {
1410+
-> (P<ast::Pat>, Vec<(Span, Option<Ident>,
1411+
P<Expr>,
1412+
&'a [ast::Attribute])>) {
14071413
if struct_def.fields.is_empty() {
14081414
return (cx.pat_enum(self.span, struct_path, vec![]), vec![]);
14091415
}
@@ -1433,15 +1439,16 @@ impl<'a> TraitDef<'a> {
14331439
paths.push(codemap::Spanned{span: sp, node: ident});
14341440
let val = cx.expr(
14351441
sp, ast::ExprParen(cx.expr_deref(sp, cx.expr_path(cx.path_ident(sp,ident)))));
1436-
ident_expr.push((sp, opt_id, val));
1442+
ident_expr.push((sp, opt_id, val, &struct_field.node.attrs[..]));
14371443
}
14381444

14391445
let subpats = self.create_subpatterns(cx, paths, mutbl);
14401446

14411447
// struct_type is definitely not Unknown, since struct_def.fields
14421448
// must be nonempty to reach here
14431449
let pattern = if struct_type == Record {
1444-
let field_pats = subpats.into_iter().zip(ident_expr.iter()).map(|(pat, &(_, id, _))| {
1450+
let field_pats = subpats.into_iter().zip(ident_expr.iter())
1451+
.map(|(pat, &(_, id, _, _))| {
14451452
// id is guaranteed to be Some
14461453
codemap::Spanned {
14471454
span: pat.span,
@@ -1459,10 +1466,10 @@ impl<'a> TraitDef<'a> {
14591466
fn create_enum_variant_pattern(&self,
14601467
cx: &mut ExtCtxt,
14611468
enum_ident: ast::Ident,
1462-
variant: &ast::Variant,
1469+
variant: &'a ast::Variant,
14631470
prefix: &str,
14641471
mutbl: ast::Mutability)
1465-
-> (P<ast::Pat>, Vec<(Span, Option<Ident>, P<Expr>)>) {
1472+
-> (P<ast::Pat>, Vec<(Span, Option<Ident>, P<Expr>, &'a [ast::Attribute])>) {
14661473
let variant_ident = variant.node.name;
14671474
let variant_path = cx.path(variant.span, vec![enum_ident, variant_ident]);
14681475
match variant.node.kind {
@@ -1472,15 +1479,15 @@ impl<'a> TraitDef<'a> {
14721479
}
14731480

14741481
let mut paths = Vec::new();
1475-
let mut ident_expr = Vec::new();
1482+
let mut ident_expr: Vec<(_, _, _, &'a [ast::Attribute])> = Vec::new();
14761483
for (i, va) in variant_args.iter().enumerate() {
14771484
let sp = self.set_expn_info(cx, va.ty.span);
14781485
let ident = cx.ident_of(&format!("{}_{}", prefix, i));
14791486
let path1 = codemap::Spanned{span: sp, node: ident};
14801487
paths.push(path1);
14811488
let expr_path = cx.expr_path(cx.path_ident(sp, ident));
14821489
let val = cx.expr(sp, ast::ExprParen(cx.expr_deref(sp, expr_path)));
1483-
ident_expr.push((sp, None, val));
1490+
ident_expr.push((sp, None, val, &[]));
14841491
}
14851492

14861493
let subpats = self.create_subpatterns(cx, paths, mutbl);

src/test/auxiliary/custom_derive_plugin.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ fn expand(cx: &mut ExtCtxt,
5555
ret_ty: Literal(Path::new_local("isize")),
5656
attributes: vec![],
5757
combine_substructure: combine_substructure(box |cx, span, substr| {
58-
let zero = cx.expr_int(span, 0);
58+
let zero = cx.expr_isize(span, 0);
5959
cs_fold(false,
6060
|cx, span, subexpr, field, _| {
6161
cx.expr_binary(span, ast::BiAdd, subexpr,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// force-host
12+
13+
#![feature(plugin_registrar)]
14+
#![feature(box_syntax)]
15+
#![feature(rustc_private)]
16+
17+
extern crate syntax;
18+
extern crate rustc;
19+
20+
use syntax::ast;
21+
use syntax::attr::AttrMetaMethods;
22+
use syntax::codemap::Span;
23+
use syntax::ext::base::{Decorator, ExtCtxt};
24+
use syntax::ext::build::AstBuilder;
25+
use syntax::ext::deriving::generic::{cs_fold, TraitDef, MethodDef, combine_substructure};
26+
use syntax::ext::deriving::generic::{Substructure, Struct, EnumMatching};
27+
use syntax::ext::deriving::generic::ty::{Literal, LifetimeBounds, Path, borrowed_explicit_self};
28+
use syntax::parse::token;
29+
use syntax::ptr::P;
30+
use rustc::plugin::Registry;
31+
32+
#[plugin_registrar]
33+
pub fn plugin_registrar(reg: &mut Registry) {
34+
reg.register_syntax_extension(
35+
token::intern("derive_TotalSum"),
36+
Decorator(box expand));
37+
}
38+
39+
fn expand(cx: &mut ExtCtxt,
40+
span: Span,
41+
mitem: &ast::MetaItem,
42+
item: &ast::Item,
43+
push: &mut FnMut(P<ast::Item>)) {
44+
let trait_def = TraitDef {
45+
span: span,
46+
attributes: vec![],
47+
path: Path::new(vec!["TotalSum"]),
48+
additional_bounds: vec![],
49+
generics: LifetimeBounds::empty(),
50+
associated_types: vec![],
51+
methods: vec![
52+
MethodDef {
53+
name: "total_sum",
54+
generics: LifetimeBounds::empty(),
55+
explicit_self: borrowed_explicit_self(),
56+
args: vec![],
57+
ret_ty: Literal(Path::new_local("isize")),
58+
attributes: vec![],
59+
combine_substructure: combine_substructure(Box::new(totalsum_substructure)),
60+
},
61+
],
62+
};
63+
64+
trait_def.expand(cx, mitem, item, push)
65+
}
66+
67+
// Mostly copied from syntax::ext::deriving::hash
68+
/// Defines how the implementation for `trace()` is to be generated
69+
fn totalsum_substructure(cx: &mut ExtCtxt, trait_span: Span,
70+
substr: &Substructure) -> P<ast::Expr> {
71+
let fields = match *substr.fields {
72+
Struct(ref fs) | EnumMatching(_, _, ref fs) => fs,
73+
_ => cx.span_bug(trait_span, "impossible substructure")
74+
};
75+
76+
fields.iter().fold(cx.expr_isize(trait_span, 0), |acc, ref item| {
77+
if item.attrs.iter().find(|a| a.check_name("ignore")).is_some() {
78+
acc
79+
} else {
80+
cx.expr_binary(item.span, ast::BiAdd, acc,
81+
cx.expr_method_call(item.span,
82+
item.self_.clone(),
83+
substr.method_ident,
84+
Vec::new()))
85+
}
86+
})
87+
}

0 commit comments

Comments
 (0)