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.
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.
11 //! The compiler code necessary for `#[derive(Decodable)]`. See encodable.rs for more.
13 use deriving
::generic
::*;
14 use deriving
::generic
::ty
::*;
17 use syntax
::ast
::{MetaItem, Expr, MutMutable}
;
18 use syntax
::codemap
::Span
;
19 use syntax
::ext
::base
::{ExtCtxt, Annotatable}
;
20 use syntax
::ext
::build
::AstBuilder
;
21 use syntax
::parse
::token
::InternedString
;
22 use syntax
::parse
::token
;
25 pub fn expand_deriving_rustc_decodable(cx
: &mut ExtCtxt
,
29 push
: &mut FnMut(Annotatable
))
31 expand_deriving_decodable_imp(cx
, span
, mitem
, item
, push
, "rustc_serialize")
34 pub fn expand_deriving_decodable(cx
: &mut ExtCtxt
,
38 push
: &mut FnMut(Annotatable
))
40 expand_deriving_decodable_imp(cx
, span
, mitem
, item
, push
, "serialize")
43 fn expand_deriving_decodable_imp(cx
: &mut ExtCtxt
,
47 push
: &mut FnMut(Annotatable
),
50 if cx
.crate_root
!= Some("std") {
51 // FIXME(#21880): lift this requirement.
52 cx
.span_err(span
, "this trait cannot be derived with #![no_std] \
57 let trait_def
= TraitDef
{
59 attributes
: Vec
::new(),
60 path
: Path
::new_(vec
!(krate
, "Decodable"), None
, vec
!(), true),
61 additional_bounds
: Vec
::new(),
62 generics
: LifetimeBounds
::empty(),
67 generics
: LifetimeBounds
{
68 lifetimes
: Vec
::new(),
69 bounds
: vec
!(("__D", vec
!(Path
::new_(
70 vec
!(krate
, "Decoder"), None
,
74 args
: vec
!(Ptr(Box
::new(Literal(Path
::new_local("__D"))),
75 Borrowed(None
, MutMutable
))),
76 ret_ty
: Literal(Path
::new_(
77 pathvec_std
!(cx
, core
::result
::Result
),
79 vec
!(Box
::new(Self_
), Box
::new(Literal(Path
::new_(
80 vec
!["__D", "Error"], None
, vec
![], false
84 attributes
: Vec
::new(),
86 combine_substructure
: combine_substructure(Box
::new(|a
, b
, c
| {
87 decodable_substructure(a
, b
, c
, krate
)
91 associated_types
: Vec
::new(),
94 trait_def
.expand(cx
, mitem
, item
, push
)
97 fn decodable_substructure(cx
: &mut ExtCtxt
, trait_span
: Span
,
98 substr
: &Substructure
,
99 krate
: &str) -> P
<Expr
> {
100 let decoder
= substr
.nonself_args
[0].clone();
101 let recurse
= vec
!(cx
.ident_of(krate
),
102 cx
.ident_of("Decodable"),
103 cx
.ident_of("decode"));
104 let exprdecode
= cx
.expr_path(cx
.path_global(trait_span
, recurse
));
105 // throw an underscore in front to suppress unused variable warnings
106 let blkarg
= cx
.ident_of("_d");
107 let blkdecoder
= cx
.expr_ident(trait_span
, blkarg
);
109 return match *substr
.fields
{
110 StaticStruct(_
, ref summary
) => {
111 let nfields
= match *summary
{
112 Unnamed(ref fields
) => fields
.len(),
113 Named(ref fields
) => fields
.len()
115 let read_struct_field
= cx
.ident_of("read_struct_field");
117 let path
= cx
.path_ident(trait_span
, substr
.type_ident
);
118 let result
= decode_static_fields(cx
,
122 |cx
, span
, name
, field
| {
124 cx
.expr_method_call(span
, blkdecoder
.clone(), read_struct_field
,
125 vec
!(cx
.expr_str(span
, name
),
126 cx
.expr_usize(span
, field
),
127 exprdecode
.clone())))
129 let result
= cx
.expr_ok(trait_span
, result
);
130 cx
.expr_method_call(trait_span
,
132 cx
.ident_of("read_struct"),
134 cx
.expr_str(trait_span
, substr
.type_ident
.name
.as_str()),
135 cx
.expr_usize(trait_span
, nfields
),
136 cx
.lambda_expr_1(trait_span
, result
, blkarg
)
139 StaticEnum(_
, ref fields
) => {
140 let variant
= cx
.ident_of("i");
142 let mut arms
= Vec
::new();
143 let mut variants
= Vec
::new();
144 let rvariant_arg
= cx
.ident_of("read_enum_variant_arg");
146 for (i
, &(ident
, v_span
, ref parts
)) in fields
.iter().enumerate() {
147 variants
.push(cx
.expr_str(v_span
, ident
.name
.as_str()));
149 let path
= cx
.path(trait_span
, vec
![substr
.type_ident
, ident
]);
150 let decoded
= decode_static_fields(cx
,
154 |cx
, span
, _
, field
| {
155 let idx
= cx
.expr_usize(span
, field
);
157 cx
.expr_method_call(span
, blkdecoder
.clone(), rvariant_arg
,
158 vec
!(idx
, exprdecode
.clone())))
161 arms
.push(cx
.arm(v_span
,
162 vec
!(cx
.pat_lit(v_span
, cx
.expr_usize(v_span
, i
))),
166 arms
.push(cx
.arm_unreachable(trait_span
));
168 let result
= cx
.expr_ok(trait_span
,
169 cx
.expr_match(trait_span
,
170 cx
.expr_ident(trait_span
, variant
), arms
));
171 let lambda
= cx
.lambda_expr(trait_span
, vec
!(blkarg
, variant
), result
);
172 let variant_vec
= cx
.expr_vec(trait_span
, variants
);
173 let variant_vec
= cx
.expr_addr_of(trait_span
, variant_vec
);
174 let result
= cx
.expr_method_call(trait_span
, blkdecoder
,
175 cx
.ident_of("read_enum_variant"),
176 vec
!(variant_vec
, lambda
));
177 cx
.expr_method_call(trait_span
,
179 cx
.ident_of("read_enum"),
181 cx
.expr_str(trait_span
, substr
.type_ident
.name
.as_str()),
182 cx
.lambda_expr_1(trait_span
, result
, blkarg
)
185 _
=> cx
.bug("expected StaticEnum or StaticStruct in derive(Decodable)")
189 /// Create a decoder for a single enum variant/struct:
190 /// - `outer_pat_path` is the path to this enum variant/struct
191 /// - `getarg` should retrieve the `usize`-th field with name `@str`.
192 fn decode_static_fields
<F
>(cx
: &mut ExtCtxt
,
194 outer_pat_path
: ast
::Path
,
195 fields
: &StaticFields
,
198 F
: FnMut(&mut ExtCtxt
, Span
, InternedString
, usize) -> P
<Expr
>,
201 Unnamed(ref fields
) => {
202 let path_expr
= cx
.expr_path(outer_pat_path
);
203 if fields
.is_empty() {
206 let fields
= fields
.iter().enumerate().map(|(i
, &span
)| {
208 token
::intern_and_get_ident(&format
!("_field{}", i
)),
212 cx
.expr_call(trait_span
, path_expr
, fields
)
215 Named(ref fields
) => {
216 // use the field's span to get nicer error messages.
217 let fields
= fields
.iter().enumerate().map(|(i
, &(ident
, span
))| {
218 let arg
= getarg(cx
, span
, ident
.name
.as_str(), i
);
219 cx
.field_imm(span
, ident
, arg
)
221 cx
.expr_struct(trait_span
, outer_pat_path
, fields
)