Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove the wrapping mod hack #2441

Merged
merged 2 commits into from
Mar 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
96 changes: 50 additions & 46 deletions bindgen/codegen/postprocessing/merge_extern_blocks.rs
Original file line number Diff line number Diff line change
@@ -1,66 +1,70 @@
use syn::{
visit_mut::{visit_item_mod_mut, VisitMut},
Item, ItemForeignMod, ItemMod,
visit_mut::{visit_file_mut, visit_item_mod_mut, VisitMut},
File, Item, ItemForeignMod, ItemMod,
};

pub(super) fn merge_extern_blocks(item_mod: &mut ItemMod) {
Visitor.visit_item_mod_mut(item_mod)
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 {
// Keep all the extern blocks in a different `Vec` for faster search.
let mut extern_blocks = Vec::<ItemForeignMod>::new();
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 {
for item in std::mem::take(items) {
if let Item::ForeignMod(ItemForeignMod {
attrs,
abi,
brace_token,
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,
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,
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));
});
}
} else {
// If the item is not an extern block, we don't have to do anything and just
// push it back.
items.push(item);
}
}

visit_item_mod_mut(self, item_mod)
// Move all the extern blocks alongside the rest of the items.
for extern_block in extern_blocks {
items.push(Item::ForeignMod(extern_block));
}
}
27 changes: 9 additions & 18 deletions bindgen/codegen/postprocessing/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use proc_macro2::TokenStream;
use quote::ToTokens;
use syn::{parse2, ItemMod};
use syn::{parse2, File};

use crate::BindgenOptions;

Expand All @@ -12,7 +12,7 @@ use sort_semantically::sort_semantically;

struct PostProcessingPass {
should_run: fn(&BindgenOptions) -> bool,
run: fn(&mut ItemMod),
run: fn(&mut File),
}

// TODO: This can be a const fn when mutable references are allowed in const
Expand All @@ -21,7 +21,7 @@ macro_rules! pass {
($pass:ident) => {
PostProcessingPass {
should_run: |options| options.$pass,
run: |item_mod| $pass(item_mod),
run: |file| $pass(file),
}
};
}
Expand All @@ -33,34 +33,25 @@ pub(crate) fn postprocessing(
items: Vec<TokenStream>,
options: &BindgenOptions,
) -> TokenStream {
let items = items.into_iter().collect();
let require_syn = PASSES.iter().any(|pass| (pass.should_run)(options));

if !require_syn {
return items.into_iter().collect();
return items;
}
let module_wrapped_tokens =
quote!(mod wrapper_for_postprocessing_hack { #( #items )* });

// This syn business is a hack, for now. This means that we are re-parsing already
// generated code using `syn` (as opposed to `quote`) because `syn` provides us more
// control over the elements.
// One caveat is that some of the items coming from `quote`d output might have
// multiple items within them. Hence, we have to wrap the incoming in a `mod`.
// The `unwrap` here is deliberate because bindgen should generate valid rust items at all
// times.
let mut item_mod = parse2::<ItemMod>(module_wrapped_tokens).unwrap();
let mut file = parse2::<File>(items).unwrap();

for pass in PASSES {
if (pass.should_run)(options) {
(pass.run)(&mut item_mod);
(pass.run)(&mut file);
}
}

let synful_items = item_mod
.content
.map(|(_, items)| items)
.unwrap_or_default()
.into_iter()
.map(|item| item.into_token_stream());

quote! { #( #synful_items )* }
file.into_token_stream()
}
57 changes: 33 additions & 24 deletions bindgen/codegen/postprocessing/sort_semantically.rs
Original file line number Diff line number Diff line change
@@ -1,38 +1,47 @@
use syn::{
visit_mut::{visit_item_mod_mut, VisitMut},
Item, ItemMod,
visit_mut::{visit_file_mut, visit_item_mod_mut, VisitMut},
File, Item, ItemMod,
};

pub(super) fn sort_semantically(item_mod: &mut ItemMod) {
Visitor.visit_item_mod_mut(item_mod)
pub(super) fn sort_semantically(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 {
items.sort_by_key(|item| match item {
Item::Type(_) => 0,
Item::Struct(_) => 1,
Item::Const(_) => 2,
Item::Fn(_) => 3,
Item::Enum(_) => 4,
Item::Union(_) => 5,
Item::Static(_) => 6,
Item::Trait(_) => 7,
Item::TraitAlias(_) => 8,
Item::Impl(_) => 9,
Item::Mod(_) => 10,
Item::Use(_) => 11,
Item::Verbatim(_) => 12,
Item::ExternCrate(_) => 13,
Item::ForeignMod(_) => 14,
Item::Macro(_) => 15,
Item::Macro2(_) => 16,
_ => 18,
});
visit_items(items);
}
visit_item_mod_mut(self, item_mod)
}
}

fn visit_items(items: &mut [Item]) {
items.sort_by_key(|item| match item {
Item::Type(_) => 0,
Item::Struct(_) => 1,
Item::Const(_) => 2,
Item::Fn(_) => 3,
Item::Enum(_) => 4,
Item::Union(_) => 5,
Item::Static(_) => 6,
Item::Trait(_) => 7,
Item::TraitAlias(_) => 8,
Item::Impl(_) => 9,
Item::Mod(_) => 10,
Item::Use(_) => 11,
Item::Verbatim(_) => 12,
Item::ExternCrate(_) => 13,
Item::ForeignMod(_) => 14,
Item::Macro(_) => 15,
Item::Macro2(_) => 16,
_ => 18,
});
}