]> git.proxmox.com Git - rustc.git/blob - src/libsyntax/ext/deriving/primitive.rs
4fe9aefa1a4dfe51a5080b51e47a7bb76a266379
[rustc.git] / src / libsyntax / ext / deriving / primitive.rs
1 // Copyright 2012-2013 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 use ast::{MetaItem, Expr};
12 use ast;
13 use codemap::Span;
14 use ext::base::{ExtCtxt, Annotatable};
15 use ext::build::AstBuilder;
16 use ext::deriving::generic::*;
17 use ext::deriving::generic::ty::*;
18 use parse::token::InternedString;
19 use ptr::P;
20
21 pub fn expand_deriving_from_primitive(cx: &mut ExtCtxt,
22 span: Span,
23 mitem: &MetaItem,
24 item: Annotatable,
25 push: &mut FnMut(Annotatable))
26 {
27 let inline = cx.meta_word(span, InternedString::new("inline"));
28 let attrs = vec!(cx.attribute(span, inline));
29 let trait_def = TraitDef {
30 span: span,
31 attributes: Vec::new(),
32 path: path_std!(cx, core::num::FromPrimitive),
33 additional_bounds: Vec::new(),
34 generics: LifetimeBounds::empty(),
35 methods: vec!(
36 MethodDef {
37 name: "from_i64",
38 generics: LifetimeBounds::empty(),
39 explicit_self: None,
40 args: vec!(Literal(path_local!(i64))),
41 ret_ty: Literal(Path::new_(pathvec_std!(cx, core::option::Option),
42 None,
43 vec!(Box::new(Self_)),
44 true)),
45 // #[inline] liable to cause code-bloat
46 attributes: attrs.clone(),
47 combine_substructure: combine_substructure(Box::new(|c, s, sub| {
48 cs_from("i64", c, s, sub)
49 })),
50 },
51 MethodDef {
52 name: "from_u64",
53 generics: LifetimeBounds::empty(),
54 explicit_self: None,
55 args: vec!(Literal(path_local!(u64))),
56 ret_ty: Literal(Path::new_(pathvec_std!(cx, core::option::Option),
57 None,
58 vec!(Box::new(Self_)),
59 true)),
60 // #[inline] liable to cause code-bloat
61 attributes: attrs,
62 combine_substructure: combine_substructure(Box::new(|c, s, sub| {
63 cs_from("u64", c, s, sub)
64 })),
65 }
66 ),
67 associated_types: Vec::new(),
68 };
69
70 trait_def.expand(cx, mitem, &item, push)
71 }
72
73 fn cs_from(name: &str, cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> P<Expr> {
74 let n = match (substr.nonself_args.len(), substr.nonself_args.get(0)) {
75 (1, Some(o_f)) => o_f,
76 _ => cx.span_bug(trait_span, "incorrect number of arguments in `derive(FromPrimitive)`")
77 };
78
79 match *substr.fields {
80 StaticStruct(..) => {
81 cx.span_err(trait_span, "`FromPrimitive` cannot be derived for structs");
82 return cx.expr_fail(trait_span, InternedString::new(""));
83 }
84 StaticEnum(enum_def, _) => {
85 if enum_def.variants.is_empty() {
86 cx.span_err(trait_span,
87 "`FromPrimitive` cannot be derived for enums with no variants");
88 return cx.expr_fail(trait_span, InternedString::new(""));
89 }
90
91 let mut arms = Vec::new();
92
93 for variant in &enum_def.variants {
94 match variant.node.kind {
95 ast::TupleVariantKind(ref args) => {
96 if !args.is_empty() {
97 cx.span_err(trait_span,
98 "`FromPrimitive` cannot be derived for \
99 enum variants with arguments");
100 return cx.expr_fail(trait_span,
101 InternedString::new(""));
102 }
103 let span = variant.span;
104
105 // expr for `$n == $variant as $name`
106 let path = cx.path(span, vec![substr.type_ident, variant.node.name]);
107 let variant = cx.expr_path(path);
108 let ty = cx.ty_ident(span, cx.ident_of(name));
109 let cast = cx.expr_cast(span, variant.clone(), ty);
110 let guard = cx.expr_binary(span, ast::BiEq, n.clone(), cast);
111
112 // expr for `Some($variant)`
113 let body = cx.expr_some(span, variant);
114
115 // arm for `_ if $guard => $body`
116 let arm = ast::Arm {
117 attrs: vec!(),
118 pats: vec!(cx.pat_wild(span)),
119 guard: Some(guard),
120 body: body,
121 };
122
123 arms.push(arm);
124 }
125 ast::StructVariantKind(_) => {
126 cx.span_err(trait_span,
127 "`FromPrimitive` cannot be derived for enums \
128 with struct variants");
129 return cx.expr_fail(trait_span,
130 InternedString::new(""));
131 }
132 }
133 }
134
135 // arm for `_ => None`
136 let arm = ast::Arm {
137 attrs: vec!(),
138 pats: vec!(cx.pat_wild(trait_span)),
139 guard: None,
140 body: cx.expr_none(trait_span),
141 };
142 arms.push(arm);
143
144 cx.expr_match(trait_span, n.clone(), arms)
145 }
146 _ => cx.span_bug(trait_span, "expected StaticEnum in derive(FromPrimitive)")
147 }
148 }