diff options
author | Christian Poveda <christian.poveda@ferrous-systems.com> | 2022-10-06 14:52:29 -0500 |
---|---|---|
committer | Christian Poveda <christian.poveda@ferrous-systems.com> | 2022-10-06 14:52:29 -0500 |
commit | 4dd91ff6d72fb5df122e5957f31d1368e84d80a2 (patch) | |
tree | 1481911049423b9af30b28cf5acc48036d83c215 /bindgen/codegen/postprocessing | |
parent | 576fd8d424c8248726542b0951c594a9734cd02a (diff) |
Make postprocessing more robust
This is done by merging extern blocks and sorting items in every module
instead of just in the root module.
The tests were changed to use `cxx` namespaces so they effectively check
that items are manipulated correctly in every single module.
Diffstat (limited to 'bindgen/codegen/postprocessing')
-rw-r--r-- | bindgen/codegen/postprocessing/merge_extern_blocks.rs | 94 | ||||
-rw-r--r-- | bindgen/codegen/postprocessing/mod.rs | 28 | ||||
-rw-r--r-- | bindgen/codegen/postprocessing/sort_semantically.rs | 58 |
3 files changed, 107 insertions, 73 deletions
diff --git a/bindgen/codegen/postprocessing/merge_extern_blocks.rs b/bindgen/codegen/postprocessing/merge_extern_blocks.rs index 2b761494..05e7e9ef 100644 --- a/bindgen/codegen/postprocessing/merge_extern_blocks.rs +++ b/bindgen/codegen/postprocessing/merge_extern_blocks.rs @@ -1,46 +1,66 @@ -use syn::{Item, ItemForeignMod}; +use syn::{ + visit_mut::{visit_item_mod_mut, VisitMut}, + Item, ItemForeignMod, ItemMod, +}; -pub(super) fn merge_extern_blocks(items: &mut Vec<Item>) { - // Keep all the extern blocks in a different `Vec` for faster search. - let mut foreign_mods = Vec::<ItemForeignMod>::new(); +pub(super) fn merge_extern_blocks(item_mod: &mut ItemMod) { + Visitor.visit_item_mod_mut(item_mod) +} + +struct Visitor; - for item in std::mem::take(items) { - match item { - Item::ForeignMod(ItemForeignMod { - attrs, - abi, - brace_token, - items: foreign_items, - }) => { - let mut exists = false; - for foreign_mod in &mut foreign_mods { - // Check if there is a extern block with the same ABI and - // attributes. - if foreign_mod.attrs == attrs && foreign_mod.abi == abi { - // Merge the items of the two blocks. - foreign_mod.items.extend_from_slice(&foreign_items); - exists = true; - break; +impl VisitMut for Visitor { + 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(); + + 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, + }); + } + } else { + // If the item is not an extern block, we don't have to do anything and just + // push it back. + items.push(item); } - // If no existing extern block had the same ABI and attributes, store - // it. - if !exists { - foreign_mods.push(ItemForeignMod { - attrs, - abi, - brace_token, - items: foreign_items, - }); - } } - // If the item is not an extern block, we don't have to do anything. - _ => 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)); + } } - } - // Move all the extern blocks alongside the rest of the items. - for foreign_mod in foreign_mods { - items.push(Item::ForeignMod(foreign_mod)); + visit_item_mod_mut(self, item_mod) } } diff --git a/bindgen/codegen/postprocessing/mod.rs b/bindgen/codegen/postprocessing/mod.rs index c6612f2b..1d5a4983 100644 --- a/bindgen/codegen/postprocessing/mod.rs +++ b/bindgen/codegen/postprocessing/mod.rs @@ -1,6 +1,6 @@ use proc_macro2::TokenStream; use quote::ToTokens; -use syn::Item; +use syn::{parse2, ItemMod}; use crate::BindgenOptions; @@ -12,7 +12,7 @@ use sort_semantically::sort_semantically; struct PostProcessingPass { should_run: fn(&BindgenOptions) -> bool, - run: fn(&mut Vec<Item>), + run: fn(&mut ItemMod), } // TODO: This can be a const fn when mutable references are allowed in const @@ -21,7 +21,7 @@ macro_rules! pass { ($pass:ident) => { PostProcessingPass { should_run: |options| options.$pass, - run: |items| $pass(items), + run: |item_mod| $pass(item_mod), } }; } @@ -38,29 +38,29 @@ pub(crate) fn postprocessing( return items.into_iter().collect(); } let module_wrapped_tokens = - quote!(mod wrapper_for_sorting_hack { #( #items )* }); + 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 two `unwrap`s here are deliberate because - // The first one won't panic because we build the `mod` and know it is there - // The second one won't panic because we know original output has something in - // it already. - let (_, mut items) = syn::parse2::<syn::ItemMod>(module_wrapped_tokens) - .unwrap() - .content - .unwrap(); + // 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(); for pass in PASSES { if (pass.should_run)(options) { - (pass.run)(&mut items); + (pass.run)(&mut item_mod); } } - let synful_items = items.into_iter().map(|item| item.into_token_stream()); + let synful_items = item_mod + .content + .map(|(_, items)| items) + .unwrap_or_default() + .into_iter() + .map(|item| item.into_token_stream()); quote! { #( #synful_items )* } } diff --git a/bindgen/codegen/postprocessing/sort_semantically.rs b/bindgen/codegen/postprocessing/sort_semantically.rs index 96596cb0..4f23ab73 100644 --- a/bindgen/codegen/postprocessing/sort_semantically.rs +++ b/bindgen/codegen/postprocessing/sort_semantically.rs @@ -1,24 +1,38 @@ -use syn::Item; +use syn::{ + visit_mut::{visit_item_mod_mut, VisitMut}, + Item, ItemMod, +}; -pub(super) fn sort_semantically(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, - }); +pub(super) fn sort_semantically(item_mod: &mut ItemMod) { + Visitor.visit_item_mod_mut(item_mod) +} + +struct Visitor; + +impl VisitMut for Visitor { + 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_item_mod_mut(self, item_mod) + } } |