bindgen/codegen/postprocessing/
merge_extern_blocks.rs

1use syn::{
2    visit_mut::{visit_file_mut, visit_item_mod_mut, VisitMut},
3    File, Item, ItemForeignMod, ItemMod,
4};
5
6pub(super) fn merge_extern_blocks(file: &mut File) {
7    Visitor.visit_file_mut(file);
8}
9
10struct Visitor;
11
12impl VisitMut for Visitor {
13    fn visit_file_mut(&mut self, file: &mut File) {
14        visit_items(&mut file.items);
15        visit_file_mut(self, file);
16    }
17
18    fn visit_item_mod_mut(&mut self, item_mod: &mut ItemMod) {
19        if let Some((_, ref mut items)) = item_mod.content {
20            visit_items(items);
21        }
22        visit_item_mod_mut(self, item_mod);
23    }
24}
25
26fn visit_items(items: &mut Vec<Item>) {
27    // Keep all the extern blocks in a different `Vec` for faster search.
28    let mut extern_blocks = Vec::<ItemForeignMod>::new();
29
30    for item in std::mem::take(items) {
31        if let Item::ForeignMod(ItemForeignMod {
32            attrs,
33            abi,
34            brace_token,
35            unsafety,
36            items: extern_block_items,
37        }) = item
38        {
39            let mut exists = false;
40            for extern_block in &mut extern_blocks {
41                // Check if there is a extern block with the same ABI and
42                // attributes.
43                if extern_block.attrs == attrs && extern_block.abi == abi {
44                    // Merge the items of the two blocks.
45                    extern_block.items.extend_from_slice(&extern_block_items);
46                    exists = true;
47                    break;
48                }
49            }
50            // If no existing extern block had the same ABI and attributes, store
51            // it.
52            if !exists {
53                extern_blocks.push(ItemForeignMod {
54                    attrs,
55                    abi,
56                    brace_token,
57                    unsafety,
58                    items: extern_block_items,
59                });
60            }
61        } else {
62            // If the item is not an extern block, we don't have to do anything and just
63            // push it back.
64            items.push(item);
65        }
66    }
67
68    // Move all the extern blocks alongside the rest of the items.
69    for extern_block in extern_blocks {
70        items.push(Item::ForeignMod(extern_block));
71    }
72}