use syn::{ visit_mut::{visit_file_mut, visit_item_mod_mut, VisitMut}, File, Item, ItemForeignMod, ItemMod, }; pub(super) fn merge_extern_blocks(file: &mut File) { Visitor.visit_file_mut(file) } struct Visitor; impl VisitMut for Visitor { fn visit_file_mut(&mut self, file: &mut File) { visit_items(&mut file.items); visit_file_mut(self, file) } fn visit_item_mod_mut(&mut self, item_mod: &mut ItemMod) { if let Some((_, ref mut items)) = item_mod.content { visit_items(items); } visit_item_mod_mut(self, item_mod) } } fn visit_items(items: &mut Vec) { // Keep all the extern blocks in a different `Vec` for faster search. let mut extern_blocks = Vec::::new(); for item in std::mem::take(items) { if let Item::ForeignMod(ItemForeignMod { attrs, abi, brace_token, unsafety, items: extern_block_items, }) = item { let mut exists = false; for extern_block in &mut extern_blocks { // Check if there is a extern block with the same ABI and // attributes. if extern_block.attrs == attrs && extern_block.abi == abi { // Merge the items of the two blocks. extern_block.items.extend_from_slice(&extern_block_items); exists = true; break; } } // If no existing extern block had the same ABI and attributes, store // it. if !exists { extern_blocks.push(ItemForeignMod { attrs, abi, brace_token, unsafety, items: extern_block_items, }); } } else { // If the item is not an extern block, we don't have to do anything and just // push it back. items.push(item); } } // Move all the extern blocks alongside the rest of the items. for extern_block in extern_blocks { items.push(Item::ForeignMod(extern_block)); } }