Skip to content

Commit b5ec18e

Browse files
authored
Merge pull request #2282 from ferrous-systems/sovereign-module-of-syn
Move codegen postprocessing to its own module
2 parents 2963d05 + ddb319c commit b5ec18e

File tree

6 files changed

+148
-122
lines changed

6 files changed

+148
-122
lines changed

CONTRIBUTING.md

+8-8
Original file line numberDiff line numberDiff line change
@@ -323,14 +323,14 @@ generated Rust code are implemented using the [`syn`](https://docs.rs/syn) crate
323323

324324
### Implementing new options using `syn`
325325

326-
Here is a list of recommendations to be followed if a new option can be
327-
implemented using the `syn` crate:
328-
329-
- The `BindgenOptions::require_syn` method must be updated to reflect that this
330-
new option requires parsing the generated Rust code with `syn`.
331-
332-
- The implementation of the new option should be added at the end of
333-
`Bindings::generate`, inside the `if options.require_syn() { ... }` block.
326+
If a new option can be implemented using the `syn` crate it should be added to
327+
the `codegen::postprocessing` module by following these steps:
328+
329+
- Introduce a new field to `BindgenOptions` for the option.
330+
- Write a free function inside `codegen::postprocessing` implementing the
331+
option. This function with the same name of the `BindgenOptions` field.
332+
- Add a new value to the `codegen::postprocessing::PASSES` for the option using
333+
the `pass!` macro.
334334

335335
## Pull Requests and Code Reviews
336336

src/codegen/mod.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ mod error;
33
mod helpers;
44
mod impl_debug;
55
mod impl_partialeq;
6+
mod postprocessing;
67
pub mod struct_layout;
78

89
#[cfg(test)]
@@ -4439,7 +4440,7 @@ impl CodeGenerator for ObjCInterface {
44394440

44404441
pub(crate) fn codegen(
44414442
context: BindgenContext,
4442-
) -> (Vec<proc_macro2::TokenStream>, BindgenOptions, Vec<String>) {
4443+
) -> (proc_macro2::TokenStream, BindgenOptions, Vec<String>) {
44434444
context.gen(|context| {
44444445
let _t = context.timer("codegen");
44454446
let counter = Cell::new(0);
@@ -4489,7 +4490,7 @@ pub(crate) fn codegen(
44894490
result.push(dynamic_items_tokens);
44904491
}
44914492

4492-
result.items
4493+
postprocessing::postprocessing(result.items, context.options())
44934494
})
44944495
}
44954496

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
use syn::{Item, ItemForeignMod};
2+
3+
pub(super) fn merge_extern_blocks(items: &mut Vec<Item>) {
4+
// Keep all the extern blocks in a different `Vec` for faster search.
5+
let mut foreign_mods = Vec::<ItemForeignMod>::new();
6+
7+
for item in std::mem::take(items) {
8+
match item {
9+
Item::ForeignMod(ItemForeignMod {
10+
attrs,
11+
abi,
12+
brace_token,
13+
items: foreign_items,
14+
}) => {
15+
let mut exists = false;
16+
for foreign_mod in &mut foreign_mods {
17+
// Check if there is a extern block with the same ABI and
18+
// attributes.
19+
if foreign_mod.attrs == attrs && foreign_mod.abi == abi {
20+
// Merge the items of the two blocks.
21+
foreign_mod.items.extend_from_slice(&foreign_items);
22+
exists = true;
23+
break;
24+
}
25+
}
26+
// If no existing extern block had the same ABI and attributes, store
27+
// it.
28+
if !exists {
29+
foreign_mods.push(ItemForeignMod {
30+
attrs,
31+
abi,
32+
brace_token,
33+
items: foreign_items,
34+
});
35+
}
36+
}
37+
// If the item is not an extern block, we don't have to do anything.
38+
_ => items.push(item),
39+
}
40+
}
41+
42+
// Move all the extern blocks alongside the rest of the items.
43+
for foreign_mod in foreign_mods {
44+
items.push(Item::ForeignMod(foreign_mod));
45+
}
46+
}

src/codegen/postprocessing/mod.rs

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
use proc_macro2::TokenStream;
2+
use quote::ToTokens;
3+
use syn::Item;
4+
5+
use crate::BindgenOptions;
6+
7+
mod merge_extern_blocks;
8+
mod sort_semantically;
9+
10+
use merge_extern_blocks::merge_extern_blocks;
11+
use sort_semantically::sort_semantically;
12+
13+
struct PostProcessingPass {
14+
should_run: fn(&BindgenOptions) -> bool,
15+
run: fn(&mut Vec<Item>),
16+
}
17+
18+
// TODO: This can be a const fn when mutable references are allowed in const
19+
// context.
20+
macro_rules! pass {
21+
($pass:ident) => {
22+
PostProcessingPass {
23+
should_run: |options| options.$pass,
24+
run: |items| $pass(items),
25+
}
26+
};
27+
}
28+
29+
const PASSES: &[PostProcessingPass] =
30+
&[pass!(merge_extern_blocks), pass!(sort_semantically)];
31+
32+
pub(crate) fn postprocessing(
33+
items: Vec<TokenStream>,
34+
options: &BindgenOptions,
35+
) -> TokenStream {
36+
let require_syn = PASSES.iter().any(|pass| (pass.should_run)(options));
37+
if !require_syn {
38+
return items.into_iter().collect();
39+
}
40+
let module_wrapped_tokens =
41+
quote!(mod wrapper_for_sorting_hack { #( #items )* });
42+
43+
// This syn business is a hack, for now. This means that we are re-parsing already
44+
// generated code using `syn` (as opposed to `quote`) because `syn` provides us more
45+
// control over the elements.
46+
// One caveat is that some of the items coming from `quote`d output might have
47+
// multiple items within them. Hence, we have to wrap the incoming in a `mod`.
48+
// The two `unwrap`s here are deliberate because
49+
// The first one won't panic because we build the `mod` and know it is there
50+
// The second one won't panic because we know original output has something in
51+
// it already.
52+
let (_, mut items) = syn::parse2::<syn::ItemMod>(module_wrapped_tokens)
53+
.unwrap()
54+
.content
55+
.unwrap();
56+
57+
for pass in PASSES {
58+
if (pass.should_run)(options) {
59+
(pass.run)(&mut items);
60+
}
61+
}
62+
63+
let synful_items = items.into_iter().map(|item| item.into_token_stream());
64+
65+
quote! { #( #synful_items )* }
66+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
use syn::Item;
2+
3+
pub(super) fn sort_semantically(items: &mut [Item]) {
4+
items.sort_by_key(|item| match item {
5+
Item::Type(_) => 0,
6+
Item::Struct(_) => 1,
7+
Item::Const(_) => 2,
8+
Item::Fn(_) => 3,
9+
Item::Enum(_) => 4,
10+
Item::Union(_) => 5,
11+
Item::Static(_) => 6,
12+
Item::Trait(_) => 7,
13+
Item::TraitAlias(_) => 8,
14+
Item::Impl(_) => 9,
15+
Item::Mod(_) => 10,
16+
Item::Use(_) => 11,
17+
Item::Verbatim(_) => 12,
18+
Item::ExternCrate(_) => 13,
19+
Item::ForeignMod(_) => 14,
20+
Item::Macro(_) => 15,
21+
Item::Macro2(_) => 16,
22+
_ => 18,
23+
});
24+
}

src/lib.rs

+1-112
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,6 @@ use std::{env, iter};
8888
// Some convenient typedefs for a fast hash map and hash set.
8989
type HashMap<K, V> = ::rustc_hash::FxHashMap<K, V>;
9090
type HashSet<K> = ::rustc_hash::FxHashSet<K>;
91-
use quote::ToTokens;
9291
pub(crate) use std::collections::hash_map::Entry;
9392

9493
/// Default prefix for the anon fields.
@@ -2118,11 +2117,6 @@ struct BindgenOptions {
21182117
impl ::std::panic::UnwindSafe for BindgenOptions {}
21192118

21202119
impl BindgenOptions {
2121-
/// Whether any of the enabled options requires `syn`.
2122-
fn require_syn(&self) -> bool {
2123-
self.sort_semantically || self.merge_extern_blocks
2124-
}
2125-
21262120
fn build(&mut self) {
21272121
let mut regex_sets = [
21282122
&mut self.allowlisted_vars,
@@ -2555,112 +2549,7 @@ impl Bindings {
25552549
parse(&mut context)?;
25562550
}
25572551

2558-
let (items, options, warnings) = codegen::codegen(context);
2559-
2560-
let module = if options.require_syn() {
2561-
let module_wrapped_tokens =
2562-
quote!(mod wrapper_for_sorting_hack { #( #items )* });
2563-
2564-
// This syn business is a hack, for now. This means that we are re-parsing already
2565-
// generated code using `syn` (as opposed to `quote`) because `syn` provides us more
2566-
// control over the elements.
2567-
// One caveat is that some of the items coming from `quote`d output might have
2568-
// multiple items within them. Hence, we have to wrap the incoming in a `mod`.
2569-
// The two `unwrap`s here are deliberate because
2570-
// The first one won't panic because we build the `mod` and know it is there
2571-
// The second one won't panic because we know original output has something in
2572-
// it already.
2573-
let mut syn_parsed_items =
2574-
syn::parse2::<syn::ItemMod>(module_wrapped_tokens)
2575-
.unwrap()
2576-
.content
2577-
.unwrap()
2578-
.1;
2579-
2580-
if options.merge_extern_blocks {
2581-
// Here we will store all the items after deduplication.
2582-
let mut items = Vec::new();
2583-
2584-
// Keep all the extern blocks in a different `Vec` for faster search.
2585-
let mut foreign_mods = Vec::<syn::ItemForeignMod>::new();
2586-
for item in syn_parsed_items {
2587-
match item {
2588-
syn::Item::ForeignMod(syn::ItemForeignMod {
2589-
attrs,
2590-
abi,
2591-
brace_token,
2592-
items: foreign_items,
2593-
}) => {
2594-
let mut exists = false;
2595-
for foreign_mod in &mut foreign_mods {
2596-
// Check if there is a extern block with the same ABI and
2597-
// attributes.
2598-
if foreign_mod.attrs == attrs &&
2599-
foreign_mod.abi == abi
2600-
{
2601-
// Merge the items of the two blocks.
2602-
foreign_mod
2603-
.items
2604-
.extend_from_slice(&foreign_items);
2605-
exists = true;
2606-
break;
2607-
}
2608-
}
2609-
// If no existing extern block had the same ABI and attributes, store
2610-
// it.
2611-
if !exists {
2612-
foreign_mods.push(syn::ItemForeignMod {
2613-
attrs,
2614-
abi,
2615-
brace_token,
2616-
items: foreign_items,
2617-
});
2618-
}
2619-
}
2620-
// If the item is not an extern block, we don't have to do anything.
2621-
_ => items.push(item),
2622-
}
2623-
}
2624-
2625-
// Move all the extern blocks alongiside the rest of the items.
2626-
for foreign_mod in foreign_mods {
2627-
items.push(syn::Item::ForeignMod(foreign_mod));
2628-
}
2629-
2630-
syn_parsed_items = items;
2631-
}
2632-
2633-
if options.sort_semantically {
2634-
syn_parsed_items.sort_by_key(|item| match item {
2635-
syn::Item::Type(_) => 0,
2636-
syn::Item::Struct(_) => 1,
2637-
syn::Item::Const(_) => 2,
2638-
syn::Item::Fn(_) => 3,
2639-
syn::Item::Enum(_) => 4,
2640-
syn::Item::Union(_) => 5,
2641-
syn::Item::Static(_) => 6,
2642-
syn::Item::Trait(_) => 7,
2643-
syn::Item::TraitAlias(_) => 8,
2644-
syn::Item::Impl(_) => 9,
2645-
syn::Item::Mod(_) => 10,
2646-
syn::Item::Use(_) => 11,
2647-
syn::Item::Verbatim(_) => 12,
2648-
syn::Item::ExternCrate(_) => 13,
2649-
syn::Item::ForeignMod(_) => 14,
2650-
syn::Item::Macro(_) => 15,
2651-
syn::Item::Macro2(_) => 16,
2652-
_ => 18,
2653-
});
2654-
}
2655-
2656-
let synful_items = syn_parsed_items
2657-
.into_iter()
2658-
.map(|item| item.into_token_stream());
2659-
2660-
quote! { #( #synful_items )* }
2661-
} else {
2662-
quote! { #( #items )* }
2663-
};
2552+
let (module, options, warnings) = codegen::codegen(context);
26642553

26652554
Ok(Bindings {
26662555
options,

0 commit comments

Comments
 (0)