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
This commit is contained in:
commit
354d16bd72
5 changed files with 200 additions and 32 deletions
|
|
@ -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))))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
87
src/test/auxiliary/custom_derive_plugin_attr.rs
Normal file
87
src/test/auxiliary/custom_derive_plugin_attr.rs
Normal file
|
|
@ -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()))
|
||||
}
|
||||
})
|
||||
}
|
||||
74
src/test/run-pass-fulldeps/derive-totalsum-attr.rs
Normal file
74
src/test/run-pass-fulldeps/derive-totalsum-attr.rs
Normal file
|
|
@ -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);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue