libsyntax: Implement #[deriving_clone]
This commit is contained in:
parent
c639a78dc4
commit
0643df28a3
@ -155,6 +155,9 @@ pub fn syntax_expander_table() -> SyntaxEnv {
|
||||
syntax_expanders.insert(@~"deriving_iter_bytes",
|
||||
@SE(ItemDecorator(
|
||||
ext::deriving::expand_deriving_iter_bytes)));
|
||||
syntax_expanders.insert(@~"deriving_clone",
|
||||
@SE(ItemDecorator(
|
||||
ext::deriving::expand_deriving_clone)));
|
||||
|
||||
// Quasi-quoting expanders
|
||||
syntax_expanders.insert(@~"quote_tokens",
|
||||
|
@ -15,7 +15,7 @@ use core::prelude::*;
|
||||
|
||||
use ast;
|
||||
use ast::{TraitTyParamBound, Ty, and, bind_by_ref, binop, deref, enum_def};
|
||||
use ast::{enum_variant_kind, expr, expr_match, ident, item, item_};
|
||||
use ast::{enum_variant_kind, expr, expr_match, ident, impure_fn, item, item_};
|
||||
use ast::{item_enum, item_impl, item_struct, Generics};
|
||||
use ast::{m_imm, meta_item, method};
|
||||
use ast::{named_field, or, pat, pat_ident, pat_wild, public, pure_fn};
|
||||
@ -84,6 +84,18 @@ pub fn expand_deriving_iter_bytes(cx: ext_ctxt,
|
||||
expand_deriving_iter_bytes_enum_def)
|
||||
}
|
||||
|
||||
pub fn expand_deriving_clone(cx: ext_ctxt,
|
||||
span: span,
|
||||
_: @meta_item,
|
||||
in_items: ~[@item])
|
||||
-> ~[@item] {
|
||||
expand_deriving(cx,
|
||||
span,
|
||||
in_items,
|
||||
expand_deriving_clone_struct_def,
|
||||
expand_deriving_clone_enum_def)
|
||||
}
|
||||
|
||||
fn expand_deriving(cx: ext_ctxt,
|
||||
span: span,
|
||||
in_items: ~[@item],
|
||||
@ -303,6 +315,21 @@ fn create_derived_iter_bytes_impl(cx: ext_ctxt,
|
||||
create_derived_impl(cx, span, type_ident, generics, methods, trait_path)
|
||||
}
|
||||
|
||||
fn create_derived_clone_impl(cx: ext_ctxt,
|
||||
span: span,
|
||||
type_ident: ident,
|
||||
generics: &Generics,
|
||||
method: @method)
|
||||
-> @item {
|
||||
let methods = [ method ];
|
||||
let trait_path = [
|
||||
cx.ident_of(~"core"),
|
||||
cx.ident_of(~"clone"),
|
||||
cx.ident_of(~"Clone"),
|
||||
];
|
||||
create_derived_impl(cx, span, type_ident, generics, methods, trait_path)
|
||||
}
|
||||
|
||||
// Creates a method from the given set of statements conforming to the
|
||||
// signature of the `iter_bytes` method.
|
||||
fn create_iter_bytes_method(cx: ext_ctxt,
|
||||
@ -352,6 +379,58 @@ fn create_iter_bytes_method(cx: ext_ctxt,
|
||||
}
|
||||
}
|
||||
|
||||
// Creates a method from the given expression conforming to the signature of
|
||||
// the `clone` method.
|
||||
fn create_clone_method(cx: ext_ctxt,
|
||||
span: span,
|
||||
+type_ident: ast::ident,
|
||||
generics: &Generics,
|
||||
expr: @ast::expr)
|
||||
-> @method {
|
||||
// Create the type parameters of the return value.
|
||||
let mut output_ty_params = ~[];
|
||||
for generics.ty_params.each |ty_param| {
|
||||
let path = build::mk_ty_path(cx, span, ~[ ty_param.ident ]);
|
||||
output_ty_params.push(path);
|
||||
}
|
||||
|
||||
// Create the type of the return value.
|
||||
let output_type_path = build::mk_raw_path_(span,
|
||||
~[ type_ident ],
|
||||
output_ty_params);
|
||||
let output_type = ast::ty_path(output_type_path, cx.next_id());
|
||||
let output_type = @ast::Ty {
|
||||
id: cx.next_id(),
|
||||
node: output_type,
|
||||
span: span
|
||||
};
|
||||
|
||||
// Create the function declaration.
|
||||
let fn_decl = build::mk_fn_decl(~[], output_type);
|
||||
|
||||
// Create the body block.
|
||||
let body_block = build::mk_simple_block(cx, span, expr);
|
||||
|
||||
// Create the self type and method identifier.
|
||||
let self_ty = spanned { node: sty_region(m_imm), span: span };
|
||||
let method_ident = cx.ident_of(~"clone");
|
||||
|
||||
// Create the method.
|
||||
@ast::method {
|
||||
ident: method_ident,
|
||||
attrs: ~[],
|
||||
generics: ast_util::empty_generics(),
|
||||
self_ty: self_ty,
|
||||
purity: impure_fn,
|
||||
decl: fn_decl,
|
||||
body: body_block,
|
||||
id: cx.next_id(),
|
||||
span: span,
|
||||
self_id: cx.next_id(),
|
||||
vis: public,
|
||||
}
|
||||
}
|
||||
|
||||
fn create_subpatterns(cx: ext_ctxt,
|
||||
span: span,
|
||||
prefix: ~str,
|
||||
@ -372,6 +451,15 @@ fn create_subpatterns(cx: ext_ctxt,
|
||||
return dvec::unwrap(subpats);
|
||||
}
|
||||
|
||||
fn is_struct_tuple(struct_def: &struct_def) -> bool {
|
||||
struct_def.fields.len() > 0 && struct_def.fields.all(|f| {
|
||||
match f.node.kind {
|
||||
named_field(*) => false,
|
||||
unnamed_field => true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn create_enum_variant_pattern(cx: ext_ctxt,
|
||||
span: span,
|
||||
variant: &variant,
|
||||
@ -488,6 +576,16 @@ fn call_substructure_iter_bytes_method(cx: ext_ctxt,
|
||||
build::mk_stmt(cx, span, self_call)
|
||||
}
|
||||
|
||||
fn call_substructure_clone_method(cx: ext_ctxt,
|
||||
span: span,
|
||||
self_field: @expr)
|
||||
-> @expr {
|
||||
// Call the substructure method.
|
||||
let clone_ident = cx.ident_of(~"clone");
|
||||
let self_method = build::mk_access_(cx, span, self_field, clone_ident);
|
||||
build::mk_call_(cx, span, self_method, ~[])
|
||||
}
|
||||
|
||||
fn variant_arg_count(cx: ext_ctxt, span: span, variant: &variant) -> uint {
|
||||
match variant.node.kind {
|
||||
tuple_variant_kind(ref args) => args.len(),
|
||||
@ -508,21 +606,12 @@ fn expand_deriving_eq_struct_def(cx: ext_ctxt,
|
||||
let eq_ident = cx.ident_of(~"eq");
|
||||
let ne_ident = cx.ident_of(~"ne");
|
||||
|
||||
let is_struct_tuple =
|
||||
struct_def.fields.len() > 0 && struct_def.fields.all(|f| {
|
||||
match f.node.kind {
|
||||
named_field(*) => false,
|
||||
unnamed_field => true
|
||||
}
|
||||
});
|
||||
|
||||
let derive_struct_fn = if is_struct_tuple {
|
||||
let derive_struct_fn = if is_struct_tuple(struct_def) {
|
||||
expand_deriving_eq_struct_tuple_method
|
||||
} else {
|
||||
expand_deriving_eq_struct_method
|
||||
};
|
||||
|
||||
|
||||
let eq_method = derive_struct_fn(cx,
|
||||
span,
|
||||
struct_def,
|
||||
@ -618,6 +707,48 @@ fn expand_deriving_iter_bytes_enum_def(cx: ext_ctxt,
|
||||
method);
|
||||
}
|
||||
|
||||
fn expand_deriving_clone_struct_def(cx: ext_ctxt,
|
||||
span: span,
|
||||
struct_def: &struct_def,
|
||||
type_ident: ident,
|
||||
generics: &Generics)
|
||||
-> @item {
|
||||
// Create the method.
|
||||
let method = if !is_struct_tuple(struct_def) {
|
||||
expand_deriving_clone_struct_method(cx,
|
||||
span,
|
||||
struct_def,
|
||||
type_ident,
|
||||
generics)
|
||||
} else {
|
||||
expand_deriving_clone_tuple_struct_method(cx,
|
||||
span,
|
||||
struct_def,
|
||||
type_ident,
|
||||
generics)
|
||||
};
|
||||
|
||||
// Create the implementation.
|
||||
create_derived_clone_impl(cx, span, type_ident, generics, method)
|
||||
}
|
||||
|
||||
fn expand_deriving_clone_enum_def(cx: ext_ctxt,
|
||||
span: span,
|
||||
enum_definition: &enum_def,
|
||||
type_ident: ident,
|
||||
generics: &Generics)
|
||||
-> @item {
|
||||
// Create the method.
|
||||
let method = expand_deriving_clone_enum_method(cx,
|
||||
span,
|
||||
enum_definition,
|
||||
type_ident,
|
||||
generics);
|
||||
|
||||
// Create the implementation.
|
||||
create_derived_clone_impl(cx, span, type_ident, generics, method)
|
||||
}
|
||||
|
||||
fn expand_deriving_eq_struct_method(cx: ext_ctxt,
|
||||
span: span,
|
||||
struct_def: &struct_def,
|
||||
@ -709,6 +840,93 @@ fn expand_deriving_iter_bytes_struct_method(cx: ext_ctxt,
|
||||
return create_iter_bytes_method(cx, span, statements);
|
||||
}
|
||||
|
||||
fn expand_deriving_clone_struct_method(cx: ext_ctxt,
|
||||
span: span,
|
||||
struct_def: &struct_def,
|
||||
type_ident: ident,
|
||||
generics: &Generics)
|
||||
-> @method {
|
||||
let self_ident = cx.ident_of(~"self");
|
||||
|
||||
// Create the new fields.
|
||||
let mut fields = ~[];
|
||||
for struct_def.fields.each |struct_field| {
|
||||
match struct_field.node.kind {
|
||||
named_field(ident, _, _) => {
|
||||
// Create the accessor for this field.
|
||||
let self_field = build::mk_access(cx,
|
||||
span,
|
||||
~[ self_ident ],
|
||||
ident);
|
||||
|
||||
// Call the substructure method.
|
||||
let call = call_substructure_clone_method(cx,
|
||||
span,
|
||||
self_field);
|
||||
|
||||
let field = build::Field { ident: ident, ex: call };
|
||||
fields.push(field);
|
||||
}
|
||||
unnamed_field => {
|
||||
cx.span_bug(span,
|
||||
~"unnamed fields in \
|
||||
expand_deriving_clone_struct_method");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create the struct literal.
|
||||
let struct_literal = build::mk_struct_e(cx,
|
||||
span,
|
||||
~[ type_ident ],
|
||||
fields);
|
||||
create_clone_method(cx, span, type_ident, generics, struct_literal)
|
||||
}
|
||||
|
||||
fn expand_deriving_clone_tuple_struct_method(cx: ext_ctxt,
|
||||
span: span,
|
||||
struct_def: &struct_def,
|
||||
type_ident: ident,
|
||||
generics: &Generics)
|
||||
-> @method {
|
||||
// Create the pattern for the match.
|
||||
let matching_path = build::mk_raw_path(span, ~[ type_ident ]);
|
||||
let field_count = struct_def.fields.len();
|
||||
let subpats = create_subpatterns(cx, span, ~"__self", field_count);
|
||||
let pat = build::mk_pat_enum(cx, span, matching_path, subpats);
|
||||
|
||||
// Create the new fields.
|
||||
let mut subcalls = ~[];
|
||||
for uint::range(0, struct_def.fields.len()) |i| {
|
||||
// Create the expression for this field.
|
||||
let field_ident = cx.ident_of(~"__self" + i.to_str());
|
||||
let field = build::mk_path(cx, span, ~[ field_ident ]);
|
||||
|
||||
// Call the substructure method.
|
||||
let subcall = call_substructure_clone_method(cx, span, field);
|
||||
subcalls.push(subcall);
|
||||
}
|
||||
|
||||
// Create the call to the struct constructor.
|
||||
let call = build::mk_call(cx, span, ~[ type_ident ], subcalls);
|
||||
|
||||
// Create the pattern body.
|
||||
let match_body_block = build::mk_simple_block(cx, span, call);
|
||||
|
||||
// Create the arm.
|
||||
let arm = ast::arm {
|
||||
pats: ~[ pat ],
|
||||
guard: None,
|
||||
body: match_body_block
|
||||
};
|
||||
|
||||
// Create the method body.
|
||||
let self_match_expr = expand_enum_or_struct_match(cx, span, ~[ arm ]);
|
||||
|
||||
// Create the method.
|
||||
create_clone_method(cx, span, type_ident, generics, self_match_expr)
|
||||
}
|
||||
|
||||
fn expand_deriving_eq_enum_method(cx: ext_ctxt,
|
||||
span: span,
|
||||
enum_definition: &enum_def,
|
||||
@ -904,6 +1122,17 @@ fn expand_deriving_eq_struct_tuple_method(cx: ext_ctxt,
|
||||
type_ident, generics, self_match_expr)
|
||||
}
|
||||
|
||||
fn expand_enum_or_struct_match(cx: ext_ctxt,
|
||||
span: span,
|
||||
arms: ~[ ast::arm ])
|
||||
-> @expr {
|
||||
let self_ident = cx.ident_of(~"self");
|
||||
let self_expr = build::mk_path(cx, span, ~[ self_ident ]);
|
||||
let self_expr = build::mk_unary(cx, span, deref, self_expr);
|
||||
let self_match_expr = expr_match(self_expr, arms);
|
||||
build::mk_expr(cx, span, self_match_expr)
|
||||
}
|
||||
|
||||
fn expand_deriving_iter_bytes_enum_method(cx: ext_ctxt,
|
||||
span: span,
|
||||
enum_definition: &enum_def)
|
||||
@ -953,14 +1182,54 @@ fn expand_deriving_iter_bytes_enum_method(cx: ext_ctxt,
|
||||
};
|
||||
|
||||
// Create the method body.
|
||||
let self_ident = cx.ident_of(~"self");
|
||||
let self_expr = build::mk_path(cx, span, ~[ self_ident ]);
|
||||
let self_expr = build::mk_unary(cx, span, deref, self_expr);
|
||||
let self_match_expr = expr_match(self_expr, arms);
|
||||
let self_match_expr = build::mk_expr(cx, span, self_match_expr);
|
||||
let self_match_expr = expand_enum_or_struct_match(cx, span, arms);
|
||||
let self_match_stmt = build::mk_stmt(cx, span, self_match_expr);
|
||||
|
||||
// Create the method.
|
||||
create_iter_bytes_method(cx, span, ~[ self_match_stmt ])
|
||||
}
|
||||
|
||||
fn expand_deriving_clone_enum_method(cx: ext_ctxt,
|
||||
span: span,
|
||||
enum_definition: &enum_def,
|
||||
type_ident: ident,
|
||||
generics: &Generics)
|
||||
-> @method {
|
||||
// Create the arms of the match in the method body.
|
||||
let arms = do enum_definition.variants.map |variant| {
|
||||
// Create the matching pattern.
|
||||
let pat = create_enum_variant_pattern(cx, span, variant, ~"__self");
|
||||
|
||||
// Iterate over the variant arguments, creating the subcalls.
|
||||
let mut subcalls = ~[];
|
||||
for uint::range(0, variant_arg_count(cx, span, variant)) |j| {
|
||||
// Create the expression for this field.
|
||||
let field_ident = cx.ident_of(~"__self" + j.to_str());
|
||||
let field = build::mk_path(cx, span, ~[ field_ident ]);
|
||||
|
||||
// Call the substructure method.
|
||||
let subcall = call_substructure_clone_method(cx, span, field);
|
||||
subcalls.push(subcall);
|
||||
}
|
||||
|
||||
// Create the call to the enum variant (if necessary).
|
||||
let call = if subcalls.len() > 0 {
|
||||
build::mk_call(cx, span, ~[ variant.node.name ], subcalls)
|
||||
} else {
|
||||
build::mk_path(cx, span, ~[ variant.node.name ])
|
||||
};
|
||||
|
||||
// Create the pattern body.
|
||||
let match_body_block = build::mk_simple_block(cx, span, call);
|
||||
|
||||
// Create the arm.
|
||||
ast::arm { pats: ~[ pat ], guard: None, body: match_body_block }
|
||||
};
|
||||
|
||||
// Create the method body.
|
||||
let self_match_expr = expand_enum_or_struct_match(cx, span, arms);
|
||||
|
||||
// Create the method.
|
||||
create_clone_method(cx, span, type_ident, generics, self_match_expr)
|
||||
}
|
||||
|
||||
|
9
src/test/run-pass/deriving-clone-enum.rs
Normal file
9
src/test/run-pass/deriving-clone-enum.rs
Normal file
@ -0,0 +1,9 @@
|
||||
#[deriving_clone]
|
||||
enum E {
|
||||
A,
|
||||
B(()),
|
||||
C
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
9
src/test/run-pass/deriving-clone-generic-enum.rs
Normal file
9
src/test/run-pass/deriving-clone-generic-enum.rs
Normal file
@ -0,0 +1,9 @@
|
||||
#[deriving_clone]
|
||||
enum E<T,U> {
|
||||
A(T),
|
||||
B(T,U),
|
||||
C
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
9
src/test/run-pass/deriving-clone-generic-struct.rs
Normal file
9
src/test/run-pass/deriving-clone-generic-struct.rs
Normal file
@ -0,0 +1,9 @@
|
||||
#[deriving_clone]
|
||||
struct S<T> {
|
||||
foo: (),
|
||||
bar: (),
|
||||
baz: T,
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
5
src/test/run-pass/deriving-clone-generic-tuple-struct.rs
Normal file
5
src/test/run-pass/deriving-clone-generic-tuple-struct.rs
Normal file
@ -0,0 +1,5 @@
|
||||
#[deriving_clone]
|
||||
struct S<T>(T, ());
|
||||
|
||||
fn main() {}
|
||||
|
8
src/test/run-pass/deriving-clone-struct.rs
Normal file
8
src/test/run-pass/deriving-clone-struct.rs
Normal file
@ -0,0 +1,8 @@
|
||||
#[deriving_clone]
|
||||
struct S {
|
||||
foo: (),
|
||||
bar: ()
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
5
src/test/run-pass/deriving-clone-tuple-struct.rs
Normal file
5
src/test/run-pass/deriving-clone-tuple-struct.rs
Normal file
@ -0,0 +1,5 @@
|
||||
#[deriving_clone]
|
||||
struct S((), ());
|
||||
|
||||
fn main() {}
|
||||
|
Loading…
Reference in New Issue
Block a user