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