]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/ext/deriving/show.rs
remove `get_ident` and `get_name`, make `as_str` sound
[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, Expr,};
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;
19 use ptr::P;
20
21 pub fn expand_deriving_show(cx: &mut ExtCtxt,
22                             span: Span,
23                             mitem: &MetaItem,
24                             item: &Annotatable,
25                             push: &mut FnMut(Annotatable))
26 {
27     // &mut ::std::fmt::Formatter
28     let fmtr = Ptr(Box::new(Literal(path_std!(cx, core::fmt::Formatter))),
29                    Borrowed(None, ast::MutMutable));
30
31     let trait_def = TraitDef {
32         span: span,
33         attributes: Vec::new(),
34         path: path_std!(cx, core::fmt::Debug),
35         additional_bounds: Vec::new(),
36         generics: LifetimeBounds::empty(),
37         methods: vec![
38             MethodDef {
39                 name: "fmt",
40                 generics: LifetimeBounds::empty(),
41                 explicit_self: borrowed_explicit_self(),
42                 args: vec!(fmtr),
43                 ret_ty: Literal(path_std!(cx, core::fmt::Result)),
44                 attributes: Vec::new(),
45                 is_unsafe: false,
46                 combine_substructure: combine_substructure(Box::new(|a, b, c| {
47                     show_substructure(a, b, c)
48                 }))
49             }
50         ],
51         associated_types: Vec::new(),
52     };
53     trait_def.expand(cx, mitem, item, push)
54 }
55
56 /// We use the debug builders to do the heavy lifting here
57 fn show_substructure(cx: &mut ExtCtxt, span: Span,
58                      substr: &Substructure) -> P<Expr> {
59     // build fmt.debug_struct(<name>).field(<fieldname>, &<fieldval>)....build()
60     // or fmt.debug_tuple(<name>).field(&<fieldval>)....build()
61     // based on the "shape".
62     let ident = match *substr.fields {
63         Struct(_) => substr.type_ident,
64         EnumMatching(_, v, _) => v.node.name,
65         EnumNonMatchingCollapsed(..) | StaticStruct(..) | StaticEnum(..) => {
66             cx.span_bug(span, "nonsensical .fields in `#[derive(Debug)]`")
67         }
68     };
69
70     // We want to make sure we have the expn_id set so that we can use unstable methods
71     let span = Span { expn_id: cx.backtrace(), .. span };
72     let name = cx.expr_lit(span, ast::Lit_::LitStr(ident.name.as_str(),
73                                                    ast::StrStyle::CookedStr));
74     let mut expr = substr.nonself_args[0].clone();
75
76     match *substr.fields {
77         Struct(ref fields) | EnumMatching(_, _, ref fields) => {
78
79             if fields.is_empty() || fields[0].name.is_none() {
80                 // tuple struct/"normal" variant
81                 expr = cx.expr_method_call(span,
82                                            expr,
83                                            token::str_to_ident("debug_tuple"),
84                                            vec![name]);
85
86                 for field in fields {
87                     // Use double indirection to make sure this works for unsized types
88                     let field = cx.expr_addr_of(field.span, field.self_.clone());
89                     let field = cx.expr_addr_of(field.span, field);
90
91                     expr = cx.expr_method_call(span,
92                                                expr,
93                                                token::str_to_ident("field"),
94                                                vec![field]);
95                 }
96             } else {
97                 // normal struct/struct variant
98                 expr = cx.expr_method_call(span,
99                                            expr,
100                                            token::str_to_ident("debug_struct"),
101                                            vec![name]);
102
103                 for field in fields {
104                     let name = cx.expr_lit(field.span, ast::Lit_::LitStr(
105                             field.name.unwrap().name.as_str(),
106                             ast::StrStyle::CookedStr));
107
108                     // Use double indirection to make sure this works for unsized types
109                     let field = cx.expr_addr_of(field.span, field.self_.clone());
110                     let field = cx.expr_addr_of(field.span, field);
111                     expr = cx.expr_method_call(span,
112                                                expr,
113                                                token::str_to_ident("field"),
114                                                vec![name, field]);
115                 }
116             }
117         }
118         _ => unreachable!()
119     }
120
121     cx.expr_method_call(span,
122                         expr,
123                         token::str_to_ident("finish"),
124                         vec![])
125 }