1 | use syn::{ |
2 | visit_mut::{visit_file_mut, visit_item_mod_mut, VisitMut}, |
3 | File, Item, ItemForeignMod, ItemMod, |
4 | }; |
5 | |
6 | pub(super) fn merge_extern_blocks(file: &mut File) { |
7 | Visitor.visit_file_mut(file) |
8 | } |
9 | |
10 | struct Visitor; |
11 | |
12 | impl 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 | |
26 | fn 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 | |