aboutsummaryrefslogtreecommitdiffstats
path: root/components/plugins/heap_size.rs
blob: 3ab1fc814a1f8c873b34895f60e3e135b7947717 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Handles the auto-deriving for `#[derive(HeapSizeOf)]`
//!
//! This provides the `#[derive(HeapSizeOf)]` decorator, which
//! generates a `HeapSizeOf` implementation that adds up
//! calls to heap_size_of_children() for all the fields
//! of a struct or enum variant.
//!
//! Fields marked `#[ignore_heap_size_of = "reason"]` will
//! be ignored in this calculation. Providing a reason is compulsory.


use syntax::ast::*;
use syntax::attr::AttrMetaMethods;
use syntax::codemap::Span;
use syntax::ext::base::{Annotatable, ExtCtxt};
use syntax::ext::build::AstBuilder;
use syntax::ext::deriving::generic::*;
use syntax::ptr::P;

pub fn expand_heap_size(cx: &mut ExtCtxt, span: Span, mitem: &MetaItem,
                        item: &Annotatable, push: &mut FnMut(Annotatable)) {
    let trait_def = TraitDef {
        span: span,
        attributes: Vec::new(),
        path: ty::Path::new(vec!("util", "mem", "HeapSizeOf")),
        additional_bounds: Vec::new(),
        generics: ty::LifetimeBounds::empty(),
        methods: vec![
            MethodDef {
                name: "heap_size_of_children",
                generics: ty::LifetimeBounds::empty(),
                explicit_self: ty::borrowed_explicit_self(),
                args: vec!(),
                ret_ty: ty::Literal(ty::Path::new_local("usize")),
                attributes: vec!(),
                is_unsafe: false,
                combine_substructure: combine_substructure(Box::new(heap_size_substructure))
            }
        ],
        associated_types: vec![],
    };
    trait_def.expand(cx, mitem, item, push)
}

/// Defines how the implementation for `heap_size_of_children()` is to be generated.
fn heap_size_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> P<Expr> {
    let fields = match *substr.fields {
        Struct(ref fs) | EnumMatching(_, _, ref fs) => fs,
        _ => cx.span_bug(trait_span, "impossible substructure in `#[derive(HeapSizeOf)]`")
    };

    fields.iter().fold(cx.expr_usize(trait_span, 0), |acc, ref item| {
        if item.attrs.iter()
               .find(|ref a| {
                    if a.check_name("ignore_heap_size_of") {
                        match a.node.value.node {
                            MetaNameValue(..) => (),
                            _ => cx.span_err(a.span, "#[ignore_heap_size_of] \
                                                      should have an explanation, \
                                                      e.g. #[ignore_heap_size_of = \"\"]")
                        }
                        true
                    } else {
                        false
                    }
                })
               .is_some() {
            acc
        } else {
            cx.expr_binary(item.span, BiAdd, acc,
                           cx.expr_method_call(item.span,
                                               item.self_.clone(),
                                               substr.method_ident,
                                               Vec::new()))
                        }
    })
}