Skip to content

Commit 1f0c2bb

Browse files
committed
Add option to deduplicate impl blocks
1 parent 20aa65a commit 1f0c2bb

File tree

6 files changed

+175
-6
lines changed

6 files changed

+175
-6
lines changed

bindgen-tests/tests/expectations/tests/merge_impl_blocks.rs

+44
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// bindgen-flags: --merge-impl-blocks --newtype-enum '.*' --enable-cxx-namespaces -- --target=x86_64-unknown-linux
2+
int foo();
3+
enum Foo {
4+
Bar = 1 << 1,
5+
Baz = 1 << 2,
6+
Duplicated = 1 << 2,
7+
Negative = -3,
8+
};
9+
int bar();
10+
11+
namespace ns {
12+
int foo();
13+
enum Bar {
14+
B0 = 1,
15+
B1 = B0 + 3,
16+
B2 = B0 + 2,
17+
B3 = B0 - 2,
18+
};
19+
int bar();
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
use syn::{
2+
visit_mut::{visit_file_mut, visit_item_mod_mut, VisitMut},
3+
File, Item, ItemImpl, ItemMod,
4+
};
5+
6+
pub(super) fn merge_impl_blocks(file: &mut File) {
7+
Visitor.visit_file_mut(file);
8+
}
9+
10+
struct Visitor;
11+
12+
impl VisitMut for Visitor {
13+
fn visit_file_mut(&mut self, file: &mut File) {
14+
visit_items(&mut file.items);
15+
visit_file_mut(self, file);
16+
}
17+
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+
26+
fn visit_items(items: &mut Vec<Item>) {
27+
// Keep all the impl blocks in a different `Vec` for faster search.
28+
let mut impl_blocks = Vec::<ItemImpl>::new();
29+
30+
for item in std::mem::take(items) {
31+
if let Item::Impl(ItemImpl {
32+
attrs,
33+
defaultness,
34+
unsafety,
35+
impl_token,
36+
generics,
37+
trait_: None, // don't merge `impl <Trait> for T` blocks
38+
self_ty,
39+
brace_token,
40+
items: impl_block_items,
41+
}) = item
42+
{
43+
let mut exists = false;
44+
for impl_block in &mut impl_blocks {
45+
// Check if there is an equivalent impl block
46+
if impl_block.attrs == attrs
47+
&& impl_block.unsafety == unsafety
48+
&& impl_block.generics == generics
49+
&& impl_block.self_ty == self_ty
50+
{
51+
// Merge the items of the two blocks.
52+
impl_block.items.extend_from_slice(&impl_block_items);
53+
exists = true;
54+
break;
55+
}
56+
}
57+
// If no matching impl block was found, store it.
58+
if !exists {
59+
impl_blocks.push(ItemImpl {
60+
attrs,
61+
defaultness,
62+
unsafety,
63+
impl_token,
64+
generics,
65+
trait_: None,
66+
self_ty,
67+
brace_token,
68+
items: impl_block_items,
69+
});
70+
}
71+
} else {
72+
// If the item is not an mergeable impl block, we don't have to do
73+
// anything and just push it back.
74+
items.push(item);
75+
}
76+
}
77+
78+
// Move all the impl blocks alongside the rest of the items.
79+
for impl_block in impl_blocks {
80+
items.push(Item::Impl(impl_block));
81+
}
82+
}

bindgen/codegen/postprocessing/mod.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ use syn::{parse2, File};
55
use crate::BindgenOptions;
66

77
mod merge_extern_blocks;
8+
mod merge_impl_blocks;
89
mod sort_semantically;
910

1011
use merge_extern_blocks::merge_extern_blocks;
12+
use merge_impl_blocks::merge_impl_blocks;
1113
use sort_semantically::sort_semantically;
1214

1315
struct PostProcessingPass {
@@ -26,8 +28,11 @@ macro_rules! pass {
2628
};
2729
}
2830

29-
const PASSES: &[PostProcessingPass] =
30-
&[pass!(merge_extern_blocks), pass!(sort_semantically)];
31+
const PASSES: &[PostProcessingPass] = &[
32+
pass!(merge_extern_blocks),
33+
pass!(merge_impl_blocks),
34+
pass!(sort_semantically),
35+
];
3136

3237
pub(crate) fn postprocessing(
3338
items: Vec<TokenStream>,

bindgen/options/cli.rs

+9-4
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,9 @@ struct BindgenCommand {
456456
/// Deduplicates extern blocks.
457457
#[arg(long)]
458458
merge_extern_blocks: bool,
459+
/// Deduplicates `impl` blocks.
460+
#[arg(long)]
461+
merge_impl_blocks: bool,
459462
/// Overrides the ABI of functions matching REGEX. The OVERRIDE value must be of the shape REGEX=ABI where ABI can be one of C, stdcall, efiapi, fastcall, thiscall, aapcs, win64 or C-unwind<.>
460463
#[arg(long, value_name = "OVERRIDE", value_parser = parse_abi_override)]
461464
override_abi: Vec<(Abi, String)>,
@@ -640,6 +643,7 @@ where
640643
vtable_generation,
641644
sort_semantically,
642645
merge_extern_blocks,
646+
merge_impl_blocks,
643647
override_abi,
644648
wrap_unsafe_ops,
645649
clang_macro_fallback,
@@ -741,8 +745,8 @@ where
741745
}
742746

743747
fn add_derives(&self, info: &DeriveInfo<'_>) -> Vec<String> {
744-
if self.kind.map_or(true, |kind| kind == info.kind) &&
745-
self.regex_set.matches(info.name)
748+
if self.kind.map_or(true, |kind| kind == info.kind)
749+
&& self.regex_set.matches(info.name)
746750
{
747751
return self.derives.clone();
748752
}
@@ -781,8 +785,8 @@ where
781785
}
782786

783787
fn add_attributes(&self, info: &AttributeInfo<'_>) -> Vec<String> {
784-
if self.kind.map_or(true, |kind| kind == info.kind) &&
785-
self.regex_set.matches(info.name)
788+
if self.kind.map_or(true, |kind| kind == info.kind)
789+
&& self.regex_set.matches(info.name)
786790
{
787791
return self.attributes.clone();
788792
}
@@ -939,6 +943,7 @@ where
939943
vtable_generation,
940944
sort_semantically,
941945
merge_extern_blocks,
946+
merge_impl_blocks,
942947
override_abi => |b, (abi, regex)| b.override_abi(abi, regex),
943948
wrap_unsafe_ops,
944949
clang_macro_fallback => |b, _| b.clang_macro_fallback(),

bindgen/options/mod.rs

+13
Original file line numberDiff line numberDiff line change
@@ -2030,6 +2030,19 @@ options! {
20302030
},
20312031
as_args: "--merge-extern-blocks",
20322032
},
2033+
/// Whether to deduplicate `impl` blocks.
2034+
merge_impl_blocks: bool {
2035+
methods: {
2036+
/// Merge all `impl` blocks under the same module into a single one.
2037+
///
2038+
/// `impl` blocks are not merged by default.
2039+
pub fn merge_impl_blocks(mut self, doit: bool) -> Self {
2040+
self.options.merge_impl_blocks = doit;
2041+
self
2042+
}
2043+
},
2044+
as_args: "--merge-impl-blocks",
2045+
},
20332046
/// Whether to wrap unsafe operations in unsafe blocks.
20342047
wrap_unsafe_ops: bool {
20352048
methods: {

0 commit comments

Comments
 (0)