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, node:file)
16 }
17
18 fn visit_item_mod_mut(&mut self, item_mod: &mut ItemMod) {
19 if let Some((_, ref mut items: &mut Vec)) = item_mod.content {
20 visit_items(items);
21 }
22 visit_item_mod_mut(self, node: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}
73