]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/ext/deriving/show.rs
auto merge of #12651 : lucab/rust/llvmdep-ldflags, r=alexcrichton
[rust.git] / src / libsyntax / ext / deriving / show.rs
1 // Copyright 2014 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;
12 use ast::{MetaItem, Item, Expr};
13 use codemap::Span;
14 use ext::format;
15 use ext::base::ExtCtxt;
16 use ext::build::AstBuilder;
17 use ext::deriving::generic::*;
18
19 use parse::token;
20
21 use collections::HashMap;
22 use std::vec_ng::Vec;
23
24 pub fn expand_deriving_show(cx: &mut ExtCtxt,
25                             span: Span,
26                             mitem: @MetaItem,
27                             item: @Item,
28                             push: |@Item|) {
29     // &mut ::std::fmt::Formatter
30     let fmtr = Ptr(~Literal(Path::new(vec!("std", "fmt", "Formatter"))),
31                    Borrowed(None, ast::MutMutable));
32
33     let trait_def = TraitDef {
34         span: span,
35         attributes: Vec::new(),
36         path: Path::new(vec!("std", "fmt", "Show")),
37         additional_bounds: Vec::new(),
38         generics: LifetimeBounds::empty(),
39         methods: vec!(
40             MethodDef {
41                 name: "fmt",
42                 generics: LifetimeBounds::empty(),
43                 explicit_self: borrowed_explicit_self(),
44                 args: vec!(fmtr),
45                 ret_ty: Literal(Path::new(vec!("std", "fmt", "Result"))),
46                 inline: false,
47                 const_nonmatching: false,
48                 combine_substructure: show_substructure
49             }
50         )
51     };
52     trait_def.expand(cx, mitem, item, push)
53 }
54
55 // we construct a format string and then defer to std::fmt, since that
56 // knows what's up with formatting at so on.
57 fn show_substructure(cx: &mut ExtCtxt, span: Span,
58                      substr: &Substructure) -> @Expr {
59     // build `<name>`, `<name>({}, {}, ...)` or `<name> { <field>: {},
60     // <field>: {}, ... }` based on the "shape".
61     //
62     // Easy start: they all start with the name.
63     let name = match *substr.fields {
64         Struct(_) => substr.type_ident,
65         EnumMatching(_, v, _) => v.node.name,
66
67         EnumNonMatching(..) | StaticStruct(..) | StaticEnum(..) => {
68             cx.span_bug(span, "nonsensical .fields in `#[deriving(Show)]`")
69         }
70     };
71
72     let mut format_string = token::get_ident(name).get().to_owned();
73     // the internal fields we're actually formatting
74     let mut exprs = Vec::new();
75
76     // Getting harder... making the format string:
77     match *substr.fields {
78         // unit struct/nullary variant: no work necessary!
79         Struct(ref fields) if fields.len() == 0 => {}
80         EnumMatching(_, _, ref fields) if fields.len() == 0 => {}
81
82         Struct(ref fields) | EnumMatching(_, _, ref fields) => {
83             if fields.get(0).name.is_none() {
84                 // tuple struct/"normal" variant
85
86                 format_string.push_str("(");
87
88                 for (i, field) in fields.iter().enumerate() {
89                     if i != 0 { format_string.push_str(", "); }
90
91                     format_string.push_str("{}");
92
93                     exprs.push(field.self_);
94                 }
95
96                 format_string.push_str(")");
97             } else {
98                 // normal struct/struct variant
99
100                 format_string.push_str(" \\{");
101
102                 for (i, field) in fields.iter().enumerate() {
103                     if i != 0 { format_string.push_str(","); }
104
105                     let name = token::get_ident(field.name.unwrap());
106                     format_string.push_str(" ");
107                     format_string.push_str(name.get());
108                     format_string.push_str(": {}");
109
110                     exprs.push(field.self_);
111                 }
112
113                 format_string.push_str(" \\}");
114             }
115         }
116         _ => unreachable!()
117     }
118
119     // AST construction!
120     // we're basically calling
121     //
122     // format_arg!(|__args| ::std::fmt::write(fmt.buf, __args), "<format_string>", exprs...)
123     //
124     // but doing it directly via ext::format.
125     let formatter = substr.nonself_args[0];
126     let buf = cx.expr_field_access(span, formatter, cx.ident_of("buf"));
127
128     let std_write = vec!(cx.ident_of("std"), cx.ident_of("fmt"), cx.ident_of("write"));
129     let args = cx.ident_of("__args");
130     let write_call = cx.expr_call_global(span, std_write, vec!(buf, cx.expr_ident(span, args)));
131     let format_closure = cx.lambda_expr(span, vec!(args), write_call);
132
133     let s = token::intern_and_get_ident(format_string);
134     let format_string = cx.expr_str(span, s);
135
136     // phew, not our responsibility any more!
137     format::expand_preparsed_format_args(cx, span,
138                                          format_closure,
139                                          format_string, exprs, Vec::new(),
140                                          HashMap::new())
141 }