Skip to content

Commit 6dfc3e7

Browse files
amanjeevpvdrzkulp
authored
Sorting the output semantically (#2254)
Generated code needs some sorting in a way that is semantically appealing. The request[1] asks for basic sorting like "types are declared first, then all structs, then all consts, then all function signatures, etc. [1] #1743 Signed-off-by: Amanjeev Sethi <[email protected]> Co-authored-by: Christian Poveda <[email protected]> Co-authored-by: Darren Kulp <[email protected]>
1 parent ebb1ce9 commit 6dfc3e7

File tree

9 files changed

+297
-6
lines changed

9 files changed

+297
-6
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,8 @@
143143

144144
## Added
145145

146+
* new feature: `--sort-semantically` flag to sort the output in a predefined manner [(#1743)]
147+
146148
## Changed
147149

148150
* clap has been updated, new msrv is 1.57.
@@ -153,6 +155,8 @@
153155

154156
## Security
155157

158+
[(#1743)]: https://github.com/rust-lang/rust-bindgen/issues/1743
159+
156160
# 0.60.1
157161

158162
Released 2022/06/06

Cargo.lock

Lines changed: 18 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ lazycell = "1"
5757
lazy_static = "1"
5858
peeking_take_while = "0.1.2"
5959
quote = { version = "1", default-features = false }
60+
syn = { version = "1.0.99", features = ["full", "extra-traits"]}
6061
regex = { version = "1.5", default-features = false , features = ["std", "unicode"] }
6162
which = { version = "4.2.1", optional = true, default-features = false }
6263
shlex = "1"

src/lib.rs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ use std::{env, iter};
8686
// Some convenient typedefs for a fast hash map and hash set.
8787
type HashMap<K, V> = ::rustc_hash::FxHashMap<K, V>;
8888
type HashSet<K> = ::rustc_hash::FxHashSet<K>;
89+
use quote::ToTokens;
8990
pub(crate) use std::collections::hash_map::Entry;
9091

9192
/// Default prefix for the anon fields.
@@ -587,6 +588,10 @@ impl Builder {
587588
output_vector.push("--vtable-generation".into());
588589
}
589590

591+
if self.options.sort_semantically {
592+
output_vector.push("--sort-semantically".into());
593+
}
594+
590595
// Add clang arguments
591596

592597
output_vector.push("--".into());
@@ -1476,6 +1481,14 @@ impl Builder {
14761481
self
14771482
}
14781483

1484+
/// If true, enables the sorting of the output in a predefined manner
1485+
///
1486+
/// TODO: Perhaps move the sorting order out into a config
1487+
pub fn sort_semantically(mut self, doit: bool) -> Self {
1488+
self.options.sort_semantically = doit;
1489+
self
1490+
}
1491+
14791492
/// Generate the Rust bindings using the options built up thus far.
14801493
pub fn generate(mut self) -> Result<Bindings, BindgenError> {
14811494
// Add any extra arguments from the environment to the clang command line.
@@ -2005,6 +2018,9 @@ struct BindgenOptions {
20052018

20062019
/// Emit vtable functions.
20072020
vtable_generation: bool,
2021+
2022+
/// Sort the code generation
2023+
sort_semantically: bool,
20082024
}
20092025

20102026
/// TODO(emilio): This is sort of a lie (see the error message that results from
@@ -2153,6 +2169,7 @@ impl Default for BindgenOptions {
21532169
c_naming: false,
21542170
force_explicit_padding: false,
21552171
vtable_generation: false,
2172+
sort_semantically: false,
21562173
}
21572174
}
21582175
}
@@ -2438,6 +2455,60 @@ impl Bindings {
24382455

24392456
let (items, options, warnings) = codegen::codegen(context);
24402457

2458+
if options.sort_semantically {
2459+
let module_wrapped_tokens =
2460+
quote!(mod wrapper_for_sorting_hack { #( #items )* });
2461+
2462+
// This semantically sorting business is a hack, for now. This means that we are
2463+
// re-parsing already generated code using `syn` (as opposed to `quote`) because
2464+
// `syn` provides us more control over the elements.
2465+
// One caveat is that some of the items coming from `quote`d output might have
2466+
// multiple items within them. Hence, we have to wrap the incoming in a `mod`.
2467+
// The two `unwrap`s here are deliberate because
2468+
// The first one won't panic because we build the `mod` and know it is there
2469+
// The second one won't panic because we know original output has something in
2470+
// it already.
2471+
let mut syn_parsed_items =
2472+
syn::parse2::<syn::ItemMod>(module_wrapped_tokens)
2473+
.unwrap()
2474+
.content
2475+
.unwrap()
2476+
.1;
2477+
2478+
syn_parsed_items.sort_by_key(|item| match item {
2479+
syn::Item::Type(_) => 0,
2480+
syn::Item::Struct(_) => 1,
2481+
syn::Item::Const(_) => 2,
2482+
syn::Item::Fn(_) => 3,
2483+
syn::Item::Enum(_) => 4,
2484+
syn::Item::Union(_) => 5,
2485+
syn::Item::Static(_) => 6,
2486+
syn::Item::Trait(_) => 7,
2487+
syn::Item::TraitAlias(_) => 8,
2488+
syn::Item::Impl(_) => 9,
2489+
syn::Item::Mod(_) => 10,
2490+
syn::Item::Use(_) => 11,
2491+
syn::Item::Verbatim(_) => 12,
2492+
syn::Item::ExternCrate(_) => 13,
2493+
syn::Item::ForeignMod(_) => 14,
2494+
syn::Item::Macro(_) => 15,
2495+
syn::Item::Macro2(_) => 16,
2496+
_ => 18,
2497+
});
2498+
2499+
let synful_items = syn_parsed_items
2500+
.into_iter()
2501+
.map(|item| item.into_token_stream());
2502+
2503+
return Ok(Bindings {
2504+
options,
2505+
warnings,
2506+
module: quote! {
2507+
#( #synful_items )*
2508+
},
2509+
});
2510+
}
2511+
24412512
Ok(Bindings {
24422513
options,
24432514
warnings,

src/options.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,9 @@ where
515515
Arg::new("vtable-generation")
516516
.long("vtable-generation")
517517
.help("Enables generation of vtable functions."),
518+
Arg::new("sort-semantically")
519+
.long("sort-semantically")
520+
.help("Enables sorting of code generation in a predefined manner."),
518521
Arg::new("V")
519522
.long("version")
520523
.help("Prints the version, and exits"),
@@ -1000,5 +1003,9 @@ where
10001003
builder = builder.vtable_generation(true);
10011004
}
10021005

1006+
if matches.is_present("sort-semantically") {
1007+
builder = builder.sort_semantically(true);
1008+
}
1009+
10031010
Ok((builder, output, verbose))
10041011
}

tests/expectations/tests/sorted-items.rs

Lines changed: 82 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/expectations/tests/unsorted-items.rs

Lines changed: 82 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/headers/sorted-items.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// bindgen-flags: --sort-semantically -- --target=x86_64-unknown-linux
2+
3+
int foo();
4+
typedef int number;
5+
int bar(number x);
6+
struct Point
7+
{
8+
number x;
9+
number y;
10+
};
11+
struct Angle
12+
{
13+
number a;
14+
number b;
15+
};
16+
int baz(struct Point point);
17+
const number NUMBER = 42;

0 commit comments

Comments
 (0)