bindgen/codegen/postprocessing/
mod.rs

1use proc_macro2::TokenStream;
2use quote::ToTokens;
3use syn::{parse2, File};
4
5use crate::BindgenOptions;
6
7mod merge_extern_blocks;
8mod sort_semantically;
9
10use merge_extern_blocks::merge_extern_blocks;
11use sort_semantically::sort_semantically;
12
13struct PostProcessingPass {
14    should_run: fn(&BindgenOptions) -> bool,
15    run: fn(&mut File),
16}
17
18// TODO: This can be a const fn when mutable references are allowed in const
19// context.
20macro_rules! pass {
21    ($pass:ident) => {
22        PostProcessingPass {
23            should_run: |options| options.$pass,
24            run: |file| $pass(file),
25        }
26    };
27}
28
29const PASSES: &[PostProcessingPass] =
30    &[pass!(merge_extern_blocks), pass!(sort_semantically)];
31
32pub(crate) fn postprocessing(
33    items: Vec<TokenStream>,
34    options: &BindgenOptions,
35) -> TokenStream {
36    let items = items.into_iter().collect();
37    let require_syn = PASSES.iter().any(|pass| (pass.should_run)(options));
38
39    if !require_syn {
40        return items;
41    }
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    // The `unwrap` here is deliberate because bindgen should generate valid rust items at all
47    // times.
48    let mut file = parse2::<File>(items).unwrap();
49
50    for pass in PASSES {
51        if (pass.should_run)(options) {
52            (pass.run)(&mut file);
53        }
54    }
55
56    file.into_token_stream()
57}