Source code

Revision control

Copy as Markdown

Other Tools

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<Item>) {
// Keep all the extern blocks in a different `Vec` for faster search.
let mut extern_blocks = Vec::<ItemForeignMod>::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));
}
}