summaryrefslogtreecommitdiff
path: root/src/codegen/postprocessing.rs
blob: 728322980fa2c9d96799f659f07b308aba7f6b9c (plain)
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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
use proc_macro2::TokenStream;
use quote::ToTokens;
use syn::Item;

use crate::BindgenOptions;

macro_rules! decl_postprocessing {
    ($($ty:ty),*) => {
        pub(crate) fn postprocessing(
            items: Vec<TokenStream>,
            options: &BindgenOptions,
        ) -> TokenStream {
            // Whether any of the enabled options requires `syn`.
            let require_syn = $(<$ty as PostProcessing>::should_run(options))||*;

            if !require_syn {
                return items.into_iter().collect();
            }

            let module_wrapped_tokens =
                quote!(mod wrapper_for_sorting_hack { #( #items )* });

            // This syn business is a hack, for now. This means that we are re-parsing already
            // generated code using `syn` (as opposed to `quote`) because `syn` provides us more
            // control over the elements.
            // One caveat is that some of the items coming from `quote`d output might have
            // multiple items within them. Hence, we have to wrap the incoming in a `mod`.
            // The two `unwrap`s here are deliberate because
            //      The first one won't panic because we build the `mod` and know it is there
            //      The second one won't panic because we know original output has something in
            //      it already.
            let mut items =
                syn::parse2::<syn::ItemMod>(module_wrapped_tokens)
                    .unwrap()
                    .content
                    .unwrap()
                    .1;

            $(if <$ty as PostProcessing>::should_run(options) {
                <$ty as PostProcessing>::run(&mut items);
            })*

            let synful_items = items
                .into_iter()
                .map(|item| item.into_token_stream());

            quote! { #( #synful_items )* }
        }
    };
}

decl_postprocessing! {
    MergeExternBlocks,
    SortSemantically
}

trait PostProcessing {
    fn should_run(options: &BindgenOptions) -> bool;

    fn run(items: &mut Vec<Item>);
}

struct SortSemantically;

impl PostProcessing for SortSemantically {
    #[inline]
    fn should_run(options: &BindgenOptions) -> bool {
        options.sort_semantically
    }

    fn run(items: &mut Vec<Item>) {
        items.sort_by_key(|item| match item {
            Item::Type(_) => 0,
            Item::Struct(_) => 1,
            Item::Const(_) => 2,
            Item::Fn(_) => 3,
            Item::Enum(_) => 4,
            Item::Union(_) => 5,
            Item::Static(_) => 6,
            Item::Trait(_) => 7,
            Item::TraitAlias(_) => 8,
            Item::Impl(_) => 9,
            Item::Mod(_) => 10,
            Item::Use(_) => 11,
            Item::Verbatim(_) => 12,
            Item::ExternCrate(_) => 13,
            Item::ForeignMod(_) => 14,
            Item::Macro(_) => 15,
            Item::Macro2(_) => 16,
            _ => 18,
        });
    }
}

struct MergeExternBlocks;

impl PostProcessing for MergeExternBlocks {
    #[inline]
    fn should_run(options: &BindgenOptions) -> bool {
        options.merge_extern_blocks
    }

    fn run(items: &mut Vec<Item>) {
        // Keep all the extern blocks in a different `Vec` for faster search.
        let mut foreign_mods = Vec::<syn::ItemForeignMod>::new();

        for item in std::mem::take(items) {
            match item {
                Item::ForeignMod(syn::ItemForeignMod {
                    attrs,
                    abi,
                    brace_token,
                    items: foreign_items,
                }) => {
                    let mut exists = false;
                    for foreign_mod in &mut foreign_mods {
                        // Check if there is a extern block with the same ABI and
                        // attributes.
                        if foreign_mod.attrs == attrs && foreign_mod.abi == abi
                        {
                            // Merge the items of the two blocks.
                            foreign_mod.items.extend_from_slice(&foreign_items);
                            exists = true;
                            break;
                        }
                    }
                    // If no existing extern block had the same ABI and attributes, store
                    // it.
                    if !exists {
                        foreign_mods.push(syn::ItemForeignMod {
                            attrs,
                            abi,
                            brace_token,
                            items: foreign_items,
                        });
                    }
                }
                // If the item is not an extern block, we don't have to do anything.
                _ => items.push(item),
            }
        }

        // Move all the extern blocks alongiside the rest of the items.
        for foreign_mod in foreign_mods {
            items.push(Item::ForeignMod(foreign_mod));
        }
    }
}