]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/ext/deriving/primitive.rs
rollup merge of #20518: nagisa/weighted-bool
[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
71     trait_def.expand(cx, mitem, item, push)
72 }
73
74 fn cs_from(name: &str, cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> P<Expr> {
75     let n = match substr.nonself_args {
76         [ref n] => n,
77         _ => cx.span_bug(trait_span, "incorrect number of arguments in `deriving(FromPrimitive)`")
78     };
79
80     match *substr.fields {
81         StaticStruct(..) => {
82             cx.span_err(trait_span, "`FromPrimitive` cannot be derived for structs");
83             return cx.expr_fail(trait_span, InternedString::new(""));
84         }
85         StaticEnum(enum_def, _) => {
86             if enum_def.variants.is_empty() {
87                 cx.span_err(trait_span,
88                             "`FromPrimitive` cannot be derived for enums with no variants");
89                 return cx.expr_fail(trait_span, InternedString::new(""));
90             }
91
92             let mut arms = Vec::new();
93
94             for variant in enum_def.variants.iter() {
95                 match variant.node.kind {
96                     ast::TupleVariantKind(ref args) => {
97                         if !args.is_empty() {
98                             cx.span_err(trait_span,
99                                         "`FromPrimitive` cannot be derived for \
100                                         enum variants with arguments");
101                             return cx.expr_fail(trait_span,
102                                                 InternedString::new(""));
103                         }
104                         let span = variant.span;
105
106                         // expr for `$n == $variant as $name`
107                         let path = cx.path(span, vec![substr.type_ident, variant.node.name]);
108                         let variant = cx.expr_path(path);
109                         let ty = cx.ty_ident(span, cx.ident_of(name));
110                         let cast = cx.expr_cast(span, variant.clone(), ty);
111                         let guard = cx.expr_binary(span, ast::BiEq, n.clone(), cast);
112
113                         // expr for `Some($variant)`
114                         let body = cx.expr_some(span, variant);
115
116                         // arm for `_ if $guard => $body`
117                         let arm = ast::Arm {
118                             attrs: vec!(),
119                             pats: vec!(cx.pat_wild(span)),
120                             guard: Some(guard),
121                             body: body,
122                         };
123
124                         arms.push(arm);
125                     }
126                     ast::StructVariantKind(_) => {
127                         cx.span_err(trait_span,
128                                     "`FromPrimitive` cannot be derived for enums \
129                                     with struct variants");
130                         return cx.expr_fail(trait_span,
131                                             InternedString::new(""));
132                     }
133                 }
134             }
135
136             // arm for `_ => None`
137             let arm = ast::Arm {
138                 attrs: vec!(),
139                 pats: vec!(cx.pat_wild(trait_span)),
140                 guard: None,
141                 body: cx.expr_none(trait_span),
142             };
143             arms.push(arm);
144
145             cx.expr_match(trait_span, n.clone(), arms)
146         }
147         _ => cx.span_bug(trait_span, "expected StaticEnum in deriving(FromPrimitive)")
148     }
149 }