• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use syn::{
2     visit_mut::{visit_file_mut, visit_item_mod_mut, VisitMut},
3     File, Item, ItemForeignMod, ItemMod,
4 };
5 
merge_extern_blocks(file: &mut File)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 {
visit_file_mut(&mut self, file: &mut File)13     fn visit_file_mut(&mut self, file: &mut File) {
14         visit_items(&mut file.items);
15         visit_file_mut(self, file)
16     }
17 
visit_item_mod_mut(&mut self, item_mod: &mut ItemMod)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 
visit_items(items: &mut Vec<Item>)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