summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CONTRIBUTING.md16
-rw-r--r--book/src/objc.md2
-rw-r--r--src/codegen/mod.rs37
-rw-r--r--src/codegen/postprocessing/merge_extern_blocks.rs46
-rw-r--r--src/codegen/postprocessing/mod.rs66
-rw-r--r--src/codegen/postprocessing/sort_semantically.rs24
-rw-r--r--src/ir/context.rs34
-rw-r--r--src/ir/ty.rs7
-rw-r--r--src/lib.rs113
-rw-r--r--tests/expectations/tests/objc_blocklist.rs42
-rw-r--r--tests/expectations/tests/stdint_typedef.rs41
-rw-r--r--tests/headers/objc_blocklist.h9
-rw-r--r--tests/headers/stdint_typedef.h10
13 files changed, 304 insertions, 143 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 2974ba42..48e4a402 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -323,14 +323,14 @@ generated Rust code are implemented using the [`syn`](https://docs.rs/syn) crate
### Implementing new options using `syn`
-Here is a list of recommendations to be followed if a new option can be
-implemented using the `syn` crate:
-
-- The `BindgenOptions::require_syn` method must be updated to reflect that this
- new option requires parsing the generated Rust code with `syn`.
-
-- The implementation of the new option should be added at the end of
- `Bindings::generate`, inside the `if options.require_syn() { ... }` block.
+If a new option can be implemented using the `syn` crate it should be added to
+the `codegen::postprocessing` module by following these steps:
+
+- Introduce a new field to `BindgenOptions` for the option.
+- Write a free function inside `codegen::postprocessing` implementing the
+ option. This function with the same name of the `BindgenOptions` field.
+- Add a new value to the `codegen::postprocessing::PASSES` for the option using
+ the `pass!` macro.
## Pull Requests and Code Reviews
diff --git a/book/src/objc.md b/book/src/objc.md
index 60e5638d..ce6d7567 100644
--- a/book/src/objc.md
+++ b/book/src/objc.md
@@ -32,6 +32,8 @@ methods found in `NSObject`.
In order to initialize a class `Foo`, you will have to do something like `let
foo = Foo(Foo::alloc().initWithStuff())`.
+To blocklist an Objective-C method, you should add the bindgen generated method
+path (e.g. `IFoo::method` or `IFoo::class_method`) as a blocklist item.
## Supported Features
diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs
index 8eb7b013..a8a7b076 100644
--- a/src/codegen/mod.rs
+++ b/src/codegen/mod.rs
@@ -3,6 +3,7 @@ mod error;
mod helpers;
mod impl_debug;
mod impl_partialeq;
+mod postprocessing;
pub mod struct_layout;
#[cfg(test)]
@@ -4190,9 +4191,19 @@ impl CodeGenerator for Function {
fn objc_method_codegen(
ctx: &BindgenContext,
method: &ObjCMethod,
+ methods: &mut Vec<proc_macro2::TokenStream>,
class_name: Option<&str>,
+ rust_class_name: &str,
prefix: &str,
-) -> proc_macro2::TokenStream {
+) {
+ // This would ideally resolve the method into an Item, and use
+ // Item::process_before_codegen; however, ObjC methods are not currently
+ // made into function items.
+ let name = format!("{}::{}{}", rust_class_name, prefix, method.rust_name());
+ if ctx.options().blocklisted_items.matches(name) {
+ return;
+ }
+
let signature = method.signature();
let fn_args = utils::fnsig_arguments(ctx, signature);
let fn_ret = utils::fnsig_return_ty(ctx, signature);
@@ -4228,11 +4239,11 @@ fn objc_method_codegen(
let method_name =
ctx.rust_ident(format!("{}{}", prefix, method.rust_name()));
- quote! {
+ methods.push(quote! {
unsafe fn #method_name #sig where <Self as std::ops::Deref>::Target: objc::Message + Sized {
#body
}
- }
+ });
}
impl CodeGenerator for ObjCInterface {
@@ -4248,10 +4259,17 @@ impl CodeGenerator for ObjCInterface {
debug_assert!(item.is_enabled_for_codegen(ctx));
let mut impl_items = vec![];
+ let rust_class_name = item.path_for_allowlisting(ctx)[1..].join("::");
for method in self.methods() {
- let impl_item = objc_method_codegen(ctx, method, None, "");
- impl_items.push(impl_item);
+ objc_method_codegen(
+ ctx,
+ method,
+ &mut impl_items,
+ None,
+ &rust_class_name,
+ "",
+ );
}
for class_method in self.class_methods() {
@@ -4261,13 +4279,14 @@ impl CodeGenerator for ObjCInterface {
.map(|m| m.rust_name())
.any(|x| x == class_method.rust_name());
let prefix = if ambiquity { "class_" } else { "" };
- let impl_item = objc_method_codegen(
+ objc_method_codegen(
ctx,
class_method,
+ &mut impl_items,
Some(self.name()),
+ &rust_class_name,
prefix,
);
- impl_items.push(impl_item);
}
let trait_name = ctx.rust_ident(self.rust_name());
@@ -4439,7 +4458,7 @@ impl CodeGenerator for ObjCInterface {
pub(crate) fn codegen(
context: BindgenContext,
-) -> (Vec<proc_macro2::TokenStream>, BindgenOptions, Vec<String>) {
+) -> (proc_macro2::TokenStream, BindgenOptions, Vec<String>) {
context.gen(|context| {
let _t = context.timer("codegen");
let counter = Cell::new(0);
@@ -4489,7 +4508,7 @@ pub(crate) fn codegen(
result.push(dynamic_items_tokens);
}
- result.items
+ postprocessing::postprocessing(result.items, context.options())
})
}
diff --git a/src/codegen/postprocessing/merge_extern_blocks.rs b/src/codegen/postprocessing/merge_extern_blocks.rs
new file mode 100644
index 00000000..2b761494
--- /dev/null
+++ b/src/codegen/postprocessing/merge_extern_blocks.rs
@@ -0,0 +1,46 @@
+use syn::{Item, ItemForeignMod};
+
+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();
+
+ 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;
+ }
+ }
+ // 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 foreign_mod in foreign_mods {
+ items.push(Item::ForeignMod(foreign_mod));
+ }
+}
diff --git a/src/codegen/postprocessing/mod.rs b/src/codegen/postprocessing/mod.rs
new file mode 100644
index 00000000..c6612f2b
--- /dev/null
+++ b/src/codegen/postprocessing/mod.rs
@@ -0,0 +1,66 @@
+use proc_macro2::TokenStream;
+use quote::ToTokens;
+use syn::Item;
+
+use crate::BindgenOptions;
+
+mod merge_extern_blocks;
+mod sort_semantically;
+
+use merge_extern_blocks::merge_extern_blocks;
+use sort_semantically::sort_semantically;
+
+struct PostProcessingPass {
+ should_run: fn(&BindgenOptions) -> bool,
+ run: fn(&mut Vec<Item>),
+}
+
+// TODO: This can be a const fn when mutable references are allowed in const
+// context.
+macro_rules! pass {
+ ($pass:ident) => {
+ PostProcessingPass {
+ should_run: |options| options.$pass,
+ run: |items| $pass(items),
+ }
+ };
+}
+
+const PASSES: &[PostProcessingPass] =
+ &[pass!(merge_extern_blocks), pass!(sort_semantically)];
+
+pub(crate) fn postprocessing(
+ items: Vec<TokenStream>,
+ options: &BindgenOptions,
+) -> TokenStream {
+ let require_syn = PASSES.iter().any(|pass| (pass.should_run)(options));
+ if !require_syn {
+ return items.into_iter().collect();
+ }
+ let module_wrapped_tokens =
+ quote!(mod wrapper_for_sorting_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();
+
+ for pass in PASSES {
+ if (pass.should_run)(options) {
+ (pass.run)(&mut items);
+ }
+ }
+
+ let synful_items = items.into_iter().map(|item| item.into_token_stream());
+
+ quote! { #( #synful_items )* }
+}
diff --git a/src/codegen/postprocessing/sort_semantically.rs b/src/codegen/postprocessing/sort_semantically.rs
new file mode 100644
index 00000000..96596cb0
--- /dev/null
+++ b/src/codegen/postprocessing/sort_semantically.rs
@@ -0,0 +1,24 @@
+use syn::Item;
+
+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,
+ });
+}
diff --git a/src/ir/context.rs b/src/ir/context.rs
index 6f16e192..12373952 100644
--- a/src/ir/context.rs
+++ b/src/ir/context.rs
@@ -2253,24 +2253,27 @@ If you encounter an error missing from this list, please file an issue or a PR!"
// Sized integer types from <stdint.h> get mapped to Rust primitive
// types regardless of whether they are blocklisted, so ensure that
// standard traits are considered derivable for them too.
- None => match name {
- "int8_t" | "uint8_t" | "int16_t" | "uint16_t" |
- "int32_t" | "uint32_t" | "int64_t" |
- "uint64_t" | "uintptr_t" | "intptr_t" |
- "ptrdiff_t" => Some(CanDerive::Yes),
- "size_t" if self.options.size_t_is_usize => {
- Some(CanDerive::Yes)
- }
- "ssize_t" if self.options.size_t_is_usize => {
- Some(CanDerive::Yes)
- }
- _ => Some(CanDerive::No),
- },
+ None => Some(if self.is_stdint_type(name) {
+ CanDerive::Yes
+ } else {
+ CanDerive::No
+ }),
})
.unwrap_or(CanDerive::No)
})
}
+ /// Is the given type a type from <stdint.h> that corresponds to a Rust primitive type?
+ pub fn is_stdint_type(&self, name: &str) -> bool {
+ match name {
+ "int8_t" | "uint8_t" | "int16_t" | "uint16_t" | "int32_t" |
+ "uint32_t" | "int64_t" | "uint64_t" | "uintptr_t" |
+ "intptr_t" | "ptrdiff_t" => true,
+ "size_t" | "ssize_t" => self.options.size_t_is_usize,
+ _ => false,
+ }
+ }
+
/// Get a reference to the set of items we should generate.
pub fn codegen_items(&self) -> &ItemSet {
assert!(self.in_codegen_phase());
@@ -2358,7 +2361,10 @@ If you encounter an error missing from this list, please file an issue or a PR!"
TypeKind::Opaque |
TypeKind::TypeParam => return true,
_ => {}
- };
+ }
+ if self.is_stdint_type(&name) {
+ return true;
+ }
}
// Unnamed top-level enums are special and we
diff --git a/src/ir/ty.rs b/src/ir/ty.rs
index c85bc687..6a3fd0e8 100644
--- a/src/ir/ty.rs
+++ b/src/ir/ty.rs
@@ -1206,6 +1206,13 @@ impl Trace for Type {
where
T: Tracer,
{
+ if self
+ .name()
+ .map_or(false, |name| context.is_stdint_type(name))
+ {
+ // These types are special-cased in codegen and don't need to be traversed.
+ return;
+ }
match *self.kind() {
TypeKind::Pointer(inner) |
TypeKind::Reference(inner) |
diff --git a/src/lib.rs b/src/lib.rs
index 81a58116..b2467fbc 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -89,7 +89,6 @@ use std::{env, iter};
// Some convenient typedefs for a fast hash map and hash set.
type HashMap<K, V> = ::rustc_hash::FxHashMap<K, V>;
type HashSet<K> = ::rustc_hash::FxHashSet<K>;
-use quote::ToTokens;
pub(crate) use std::collections::hash_map::Entry;
/// Default prefix for the anon fields.
@@ -2114,11 +2113,6 @@ struct BindgenOptions {
impl ::std::panic::UnwindSafe for BindgenOptions {}
impl BindgenOptions {
- /// Whether any of the enabled options requires `syn`.
- fn require_syn(&self) -> bool {
- self.sort_semantically || self.merge_extern_blocks
- }
-
fn build(&mut self) {
let mut regex_sets = [
&mut self.allowlisted_vars,
@@ -2551,112 +2545,7 @@ impl Bindings {
parse(&mut context)?;
}
- let (items, options, warnings) = codegen::codegen(context);
-
- let module = if options.require_syn() {
- let module_wrapped_tokens =
- quote!(mod wrapper_for_sorting_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 syn_parsed_items =
- syn::parse2::<syn::ItemMod>(module_wrapped_tokens)
- .unwrap()
- .content
- .unwrap()
- .1;
-
- if options.merge_extern_blocks {
- // Here we will store all the items after deduplication.
- let mut items = Vec::new();
-
- // Keep all the extern blocks in a different `Vec` for faster search.
- let mut foreign_mods = Vec::<syn::ItemForeignMod>::new();
- for item in syn_parsed_items {
- match item {
- syn::Item::ForeignMod(syn::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;
- }
- }
- // If no existing extern block had the same ABI and attributes, store
- // it.
- if !exists {
- foreign_mods.push(syn::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 alongiside the rest of the items.
- for foreign_mod in foreign_mods {
- items.push(syn::Item::ForeignMod(foreign_mod));
- }
-
- syn_parsed_items = items;
- }
-
- if options.sort_semantically {
- syn_parsed_items.sort_by_key(|item| match item {
- syn::Item::Type(_) => 0,
- syn::Item::Struct(_) => 1,
- syn::Item::Const(_) => 2,
- syn::Item::Fn(_) => 3,
- syn::Item::Enum(_) => 4,
- syn::Item::Union(_) => 5,
- syn::Item::Static(_) => 6,
- syn::Item::Trait(_) => 7,
- syn::Item::TraitAlias(_) => 8,
- syn::Item::Impl(_) => 9,
- syn::Item::Mod(_) => 10,
- syn::Item::Use(_) => 11,
- syn::Item::Verbatim(_) => 12,
- syn::Item::ExternCrate(_) => 13,
- syn::Item::ForeignMod(_) => 14,
- syn::Item::Macro(_) => 15,
- syn::Item::Macro2(_) => 16,
- _ => 18,
- });
- }
-
- let synful_items = syn_parsed_items
- .into_iter()
- .map(|item| item.into_token_stream());
-
- quote! { #( #synful_items )* }
- } else {
- quote! { #( #items )* }
- };
+ let (module, options, warnings) = codegen::codegen(context);
Ok(Bindings {
options,
diff --git a/tests/expectations/tests/objc_blocklist.rs b/tests/expectations/tests/objc_blocklist.rs
new file mode 100644
index 00000000..7d5d19b0
--- /dev/null
+++ b/tests/expectations/tests/objc_blocklist.rs
@@ -0,0 +1,42 @@
+#![allow(
+ dead_code,
+ non_snake_case,
+ non_camel_case_types,
+ non_upper_case_globals
+)]
+#![cfg(target_os = "macos")]
+
+#[macro_use]
+extern crate objc;
+#[allow(non_camel_case_types)]
+pub type id = *mut objc::runtime::Object;
+#[repr(transparent)]
+#[derive(Debug, Copy, Clone)]
+pub struct SomeClass(pub id);
+impl std::ops::Deref for SomeClass {
+ type Target = objc::runtime::Object;
+ fn deref(&self) -> &Self::Target {
+ unsafe { &*self.0 }
+ }
+}
+unsafe impl objc::Message for SomeClass {}
+impl SomeClass {
+ pub fn alloc() -> Self {
+ Self(unsafe { msg_send!(class!(SomeClass), alloc) })
+ }
+}
+impl ISomeClass for SomeClass {}
+pub trait ISomeClass: Sized + std::ops::Deref {
+ unsafe fn ambiguouslyBlockedMethod(&self)
+ where
+ <Self as std::ops::Deref>::Target: objc::Message + Sized,
+ {
+ msg_send!(*self, ambiguouslyBlockedMethod)
+ }
+ unsafe fn instanceMethod(&self)
+ where
+ <Self as std::ops::Deref>::Target: objc::Message + Sized,
+ {
+ msg_send!(*self, instanceMethod)
+ }
+}
diff --git a/tests/expectations/tests/stdint_typedef.rs b/tests/expectations/tests/stdint_typedef.rs
new file mode 100644
index 00000000..a52db496
--- /dev/null
+++ b/tests/expectations/tests/stdint_typedef.rs
@@ -0,0 +1,41 @@
+#![allow(
+ dead_code,
+ non_snake_case,
+ non_camel_case_types,
+ non_upper_case_globals
+)]
+
+extern "C" {
+ pub fn fun() -> u64;
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct Struct {
+ pub field: u64,
+}
+#[test]
+fn bindgen_test_layout_Struct() {
+ const UNINIT: ::std::mem::MaybeUninit<Struct> =
+ ::std::mem::MaybeUninit::uninit();
+ let ptr = UNINIT.as_ptr();
+ assert_eq!(
+ ::std::mem::size_of::<Struct>(),
+ 8usize,
+ concat!("Size of: ", stringify!(Struct))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<Struct>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(Struct))
+ );
+ assert_eq!(
+ unsafe { ::std::ptr::addr_of!((*ptr).field) as usize - ptr as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(Struct),
+ "::",
+ stringify!(field)
+ )
+ );
+}
diff --git a/tests/headers/objc_blocklist.h b/tests/headers/objc_blocklist.h
new file mode 100644
index 00000000..605f2993
--- /dev/null
+++ b/tests/headers/objc_blocklist.h
@@ -0,0 +1,9 @@
+// bindgen-flags: --objc-extern-crate --blocklist-item ISomeClass::class_ambiguouslyBlockedMethod --blocklist-item ISomeClass::blockedInstanceMethod -- -x objective-c
+// bindgen-osx-only
+
+@interface SomeClass
++ (void)ambiguouslyBlockedMethod;
+- (void)ambiguouslyBlockedMethod;
+- (void)instanceMethod;
+- (void)blockedInstanceMethod;
+@end
diff --git a/tests/headers/stdint_typedef.h b/tests/headers/stdint_typedef.h
new file mode 100644
index 00000000..f716a7f1
--- /dev/null
+++ b/tests/headers/stdint_typedef.h
@@ -0,0 +1,10 @@
+// bindgen-flags: --allowlist-type="Struct" --allowlist-function="fun"
+
+// no typedef should be emitted for `__uint64_t`
+typedef unsigned long long __uint64_t;
+typedef __uint64_t uint64_t;
+
+uint64_t fun();
+struct Struct {
+ uint64_t field;
+};