From 0bedb67fe96b0ea40cb2e8dc54df97a20c296f35 Mon Sep 17 00:00:00 2001 From: Sebastian Imlay Date: Thu, 14 May 2020 16:56:53 -0700 Subject: [PATCH 01/15] Added Categories to objective-c inheritance. * Added items_mut to BindgenContext * Added Clone to ObjCInterface, ObjCMethod and FunctionSig :/ * Added references to the ObjCInterfaces for categories that extend a given ObjCInterface --- src/codegen/mod.rs | 20 ++++++ src/ir/context.rs | 11 ++++ src/ir/function.rs | 2 +- src/ir/objc.rs | 45 ++++++++++++- src/ir/ty.rs | 2 +- .../tests/objc_category_inheritance.rs | 66 +++++++++++++++++++ tests/headers/objc_category_inheritance.h | 13 ++++ 7 files changed, 154 insertions(+), 5 deletions(-) create mode 100644 tests/expectations/tests/objc_category_inheritance.rs create mode 100644 tests/headers/objc_category_inheritance.h diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index af1622daff..6838ac657a 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -3821,6 +3821,26 @@ impl CodeGenerator for ObjCInterface { } }; result.push(impl_trait); + for category in &parent.categories { + let category_name = + ctx.rust_ident(category.rust_name()); + let impl_trait = if category.is_template() { + let template_names: Vec = parent + .template_names + .iter() + .map(|g| ctx.rust_ident(g)) + .collect(); + quote! { + impl <#(#template_names :'static),*> #category_name <#(#template_names),*> for #class_name { + } + } + } else { + quote! { + impl #category_name for #class_name { } + } + }; + result.push(impl_trait); + } parent.parent_class } else { None diff --git a/src/ir/context.rs b/src/ir/context.rs index 8ff102c023..7271310e39 100644 --- a/src/ir/context.rs +++ b/src/ir/context.rs @@ -902,6 +902,17 @@ If you encounter an error missing from this list, please file an issue or a PR!" }) } + /// IterMut over all items that have been defined. + pub fn items_mut(&mut self) -> impl Iterator { + self.items + .iter_mut() + .enumerate() + .filter_map(|(index, item)| { + let item = item.as_mut()?; + Some((ItemId(index), item)) + }) + } + /// Have we collected all unresolved type references yet? pub fn collected_typerefs(&self) -> bool { self.collected_typerefs diff --git a/src/ir/function.rs b/src/ir/function.rs index e811a721b1..1dbdcf4160 100644 --- a/src/ir/function.rs +++ b/src/ir/function.rs @@ -210,7 +210,7 @@ impl quote::ToTokens for Abi { } /// A function signature. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct FunctionSig { /// The return type of the function. return_type: TypeId, diff --git a/src/ir/objc.rs b/src/ir/objc.rs index 91855c6776..4b8ab37a92 100644 --- a/src/ir/objc.rs +++ b/src/ir/objc.rs @@ -21,7 +21,7 @@ use proc_macro2::{Ident, Span, TokenStream}; /// Objective C interface as used in TypeKind /// /// Also protocols and categories are parsed as this type -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct ObjCInterface { /// The name /// like, NSObject @@ -37,6 +37,9 @@ pub struct ObjCInterface { /// The list of protocols that this interface conforms to. pub conforms_to: Vec, + /// The list of categories that this interface is extended by. + pub categories: Vec, + /// The direct parent for this interface. pub parent_class: Option, @@ -47,7 +50,7 @@ pub struct ObjCInterface { } /// The objective c methods -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct ObjCMethod { /// The original method selector name /// like, dataWithBytes:length: @@ -68,6 +71,7 @@ impl ObjCInterface { ObjCInterface { name: name.to_owned(), category: None, + categories: Vec::new(), is_protocol: false, template_names: Vec::new(), parent_class: None, @@ -140,9 +144,11 @@ impl ObjCInterface { CXCursor_ObjCClassRef => { if cursor.kind() == CXCursor_ObjCCategoryDecl { // We are actually a category extension, and we found the reference - // to the original interface, so name this interface approriately + // to the original interface, so name this interface approriately. + interface.name = c.spelling(); interface.category = Some(cursor.spelling()); + } } CXCursor_ObjCProtocolRef => { @@ -194,6 +200,39 @@ impl ObjCInterface { } CXChildVisit_Continue }); + + if interface.is_category() { + // If this interface is a category, we need to find the interface that this category + // extends. + let needle = format!("{}", interface.name()); + debug!( + "Category {} belongs to {}, find the item", + interface.rust_name(), + needle + ); + for (_, item) in ctx.items_mut() { + if let Some(ref mut ty) = item.kind_mut().as_type_mut() { + match ty.kind_mut() { + TypeKind::ObjCInterface(ref mut real_interface) => { + if !real_interface.is_category() { + debug!("Checking interface {}, against needle {:?}", real_interface.name, needle); + if needle == real_interface.name() { + debug!( + "Found real interface {:?}", + real_interface + ); + real_interface + .categories + .push(interface.clone()); + break; + } + } + } + _ => {} + } + } + } + } Some(interface) } diff --git a/src/ir/ty.rs b/src/ir/ty.rs index b77b5e75e9..2f1640eae7 100644 --- a/src/ir/ty.rs +++ b/src/ir/ty.rs @@ -627,7 +627,7 @@ pub enum TypeKind { /// A compound type, that is, a class, struct, or union. Comp(CompInfo), - /// An opaque type that we just don't understand. All usage of this shoulf + /// An opaque type that we just don't understand. All usage of this should /// result in an opaque blob of bytes generated from the containing type's /// layout. Opaque, diff --git a/tests/expectations/tests/objc_category_inheritance.rs b/tests/expectations/tests/objc_category_inheritance.rs new file mode 100644 index 0000000000..a3fd0aed25 --- /dev/null +++ b/tests/expectations/tests/objc_category_inheritance.rs @@ -0,0 +1,66 @@ +/* automatically generated by rust-bindgen */ + +#![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(Clone, Copy)] +pub struct Foo(pub id); +impl std::ops::Deref for Foo { + type Target = objc::runtime::Object; + fn deref(&self) -> &Self::Target { + unsafe { &*self.0 } + } +} +unsafe impl objc::Message for Foo {} +impl Foo { + pub fn alloc() -> Self { + Self(unsafe { msg_send!(objc::class!(Foo), alloc) }) + } +} +impl IFoo for Foo {} +pub trait IFoo: Sized + std::ops::Deref { + unsafe fn method(self) + where + ::Target: objc::Message + Sized, + { + msg_send!(self, method) + } +} +impl Foo_BarCategory for Foo {} +pub trait Foo_BarCategory: Sized + std::ops::Deref { + unsafe fn categoryMethod(self) + where + ::Target: objc::Message + Sized, + { + msg_send!(self, categoryMethod) + } +} +#[repr(transparent)] +#[derive(Clone, Copy)] +pub struct Bar(pub id); +impl std::ops::Deref for Bar { + type Target = objc::runtime::Object; + fn deref(&self) -> &Self::Target { + unsafe { &*self.0 } + } +} +unsafe impl objc::Message for Bar {} +impl Bar { + pub fn alloc() -> Self { + Self(unsafe { msg_send!(objc::class!(Bar), alloc) }) + } +} +impl IFoo for Bar {} +impl Foo_BarCategory for Bar {} +impl IBar for Bar {} +pub trait IBar: Sized + std::ops::Deref {} diff --git a/tests/headers/objc_category_inheritance.h b/tests/headers/objc_category_inheritance.h new file mode 100644 index 0000000000..9d31341352 --- /dev/null +++ b/tests/headers/objc_category_inheritance.h @@ -0,0 +1,13 @@ +// bindgen-flags: --objc-extern-crate -- -x objective-c +// bindgen-osx-only + +@interface Foo +-(void)method; +@end + +@interface Foo (BarCategory) +-(void)categoryMethod; +@end + +@interface Bar: Foo +@end From bcd3d7f9daafa3d857743bbfd622571b88be92ba Mon Sep 17 00:00:00 2001 From: Sebastian Imlay Date: Thu, 14 May 2020 17:20:23 -0700 Subject: [PATCH 02/15] Removed derive Clones in favor of a tupple. * Added a bunch more tests to prevent future breakage. --- src/codegen/mod.rs | 20 ++--- src/ir/function.rs | 2 +- src/ir/objc.rs | 15 ++-- .../objc_category_template_inheritance.rs | 73 +++++++++++++++++++ .../tests/objc_template_inheritance.rs | 63 ++++++++++++++++ .../objc_category_template_inheritance.h | 14 ++++ tests/headers/objc_template_inheritance.h | 10 +++ 7 files changed, 180 insertions(+), 17 deletions(-) create mode 100644 tests/expectations/tests/objc_category_template_inheritance.rs create mode 100644 tests/expectations/tests/objc_template_inheritance.rs create mode 100644 tests/headers/objc_category_template_inheritance.h create mode 100644 tests/headers/objc_template_inheritance.h diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index 6838ac657a..e60228453b 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -3821,15 +3821,17 @@ impl CodeGenerator for ObjCInterface { } }; result.push(impl_trait); - for category in &parent.categories { - let category_name = - ctx.rust_ident(category.rust_name()); - let impl_trait = if category.is_template() { - let template_names: Vec = parent - .template_names - .iter() - .map(|g| ctx.rust_ident(g)) - .collect(); + for (category_name, category_template_names) in + &parent.categories + { + let category_name = ctx.rust_ident(category_name); + let impl_trait = if !category_template_names.is_empty() + { + let template_names: Vec = + category_template_names + .iter() + .map(|g| ctx.rust_ident(g)) + .collect(); quote! { impl <#(#template_names :'static),*> #category_name <#(#template_names),*> for #class_name { } diff --git a/src/ir/function.rs b/src/ir/function.rs index 1dbdcf4160..e811a721b1 100644 --- a/src/ir/function.rs +++ b/src/ir/function.rs @@ -210,7 +210,7 @@ impl quote::ToTokens for Abi { } /// A function signature. -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct FunctionSig { /// The return type of the function. return_type: TypeId, diff --git a/src/ir/objc.rs b/src/ir/objc.rs index 4b8ab37a92..e6b48025d0 100644 --- a/src/ir/objc.rs +++ b/src/ir/objc.rs @@ -21,7 +21,7 @@ use proc_macro2::{Ident, Span, TokenStream}; /// Objective C interface as used in TypeKind /// /// Also protocols and categories are parsed as this type -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct ObjCInterface { /// The name /// like, NSObject @@ -37,8 +37,8 @@ pub struct ObjCInterface { /// The list of protocols that this interface conforms to. pub conforms_to: Vec, - /// The list of categories that this interface is extended by. - pub categories: Vec, + /// The list of categories (and the template tags) that this interface is extended by. + pub categories: Vec<(String, Vec)>, /// The direct parent for this interface. pub parent_class: Option, @@ -50,7 +50,7 @@ pub struct ObjCInterface { } /// The objective c methods -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct ObjCMethod { /// The original method selector name /// like, dataWithBytes:length: @@ -221,9 +221,10 @@ impl ObjCInterface { "Found real interface {:?}", real_interface ); - real_interface - .categories - .push(interface.clone()); + real_interface.categories.push(( + interface.rust_name(), + interface.template_names.clone(), + )); break; } } diff --git a/tests/expectations/tests/objc_category_template_inheritance.rs b/tests/expectations/tests/objc_category_template_inheritance.rs new file mode 100644 index 0000000000..f813127dd5 --- /dev/null +++ b/tests/expectations/tests/objc_category_template_inheritance.rs @@ -0,0 +1,73 @@ +/* automatically generated by rust-bindgen */ + +#![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(Clone, Copy)] +pub struct Foo(pub id); +impl std::ops::Deref for Foo { + type Target = objc::runtime::Object; + fn deref(&self) -> &Self::Target { + unsafe { &*self.0 } + } +} +unsafe impl objc::Message for Foo {} +impl Foo { + pub fn alloc() -> Self { + Self(unsafe { msg_send!(objc::class!(Foo), alloc) }) + } +} +impl IFoo for Foo {} +pub trait IFoo: Sized + std::ops::Deref { + unsafe fn get(self) -> u64 + where + ::Target: objc::Message + Sized, + { + msg_send!(self, get) + } +} +impl Foo_Baz for Foo {} +pub trait Foo_Baz: Sized + std::ops::Deref { + unsafe fn get(self) -> u64 + where + ::Target: objc::Message + Sized, + { + msg_send!(self, get) + } +} +#[repr(transparent)] +#[derive(Clone, Copy)] +pub struct Bar(pub id); +impl std::ops::Deref for Bar { + type Target = objc::runtime::Object; + fn deref(&self) -> &Self::Target { + unsafe { &*self.0 } + } +} +unsafe impl objc::Message for Bar {} +impl Bar { + pub fn alloc() -> Self { + Self(unsafe { msg_send!(objc::class!(Bar), alloc) }) + } +} +impl IFoo for Bar {} +impl Foo_Baz for Bar {} +impl IBar for Bar {} +pub trait IBar: Sized + std::ops::Deref { + unsafe fn get(self) -> u64 + where + ::Target: objc::Message + Sized, + { + msg_send!(self, get) + } +} diff --git a/tests/expectations/tests/objc_template_inheritance.rs b/tests/expectations/tests/objc_template_inheritance.rs new file mode 100644 index 0000000000..844a3600d9 --- /dev/null +++ b/tests/expectations/tests/objc_template_inheritance.rs @@ -0,0 +1,63 @@ +/* automatically generated by rust-bindgen */ + +#![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(Clone, Copy)] +pub struct Foo(pub id); +impl std::ops::Deref for Foo { + type Target = objc::runtime::Object; + fn deref(&self) -> &Self::Target { + unsafe { &*self.0 } + } +} +unsafe impl objc::Message for Foo {} +impl Foo { + pub fn alloc() -> Self { + Self(unsafe { msg_send!(objc::class!(Foo), alloc) }) + } +} +impl IFoo for Foo {} +pub trait IFoo: Sized + std::ops::Deref { + unsafe fn get(self) -> u64 + where + ::Target: objc::Message + Sized, + { + msg_send!(self, get) + } +} +#[repr(transparent)] +#[derive(Clone, Copy)] +pub struct Bar(pub id); +impl std::ops::Deref for Bar { + type Target = objc::runtime::Object; + fn deref(&self) -> &Self::Target { + unsafe { &*self.0 } + } +} +unsafe impl objc::Message for Bar {} +impl Bar { + pub fn alloc() -> Self { + Self(unsafe { msg_send!(objc::class!(Bar), alloc) }) + } +} +impl IFoo for Bar {} +impl IBar for Bar {} +pub trait IBar: Sized + std::ops::Deref { + unsafe fn get(self) -> u64 + where + ::Target: objc::Message + Sized, + { + msg_send!(self, get) + } +} diff --git a/tests/headers/objc_category_template_inheritance.h b/tests/headers/objc_category_template_inheritance.h new file mode 100644 index 0000000000..b1c4743406 --- /dev/null +++ b/tests/headers/objc_category_template_inheritance.h @@ -0,0 +1,14 @@ +// bindgen-flags: --objc-extern-crate -- -x objective-c +// bindgen-osx-only + +@interface Foo<__covariant ObjectType> +- (ObjectType)get; +@end + +@interface Foo<__covariant ObjectType> (Baz) +- (ObjectType)get; +@end + +@interface Bar<__covariant ObjectType>: Foo +- (ObjectType)get; +@end diff --git a/tests/headers/objc_template_inheritance.h b/tests/headers/objc_template_inheritance.h new file mode 100644 index 0000000000..7580d0c78f --- /dev/null +++ b/tests/headers/objc_template_inheritance.h @@ -0,0 +1,10 @@ +// bindgen-flags: --objc-extern-crate -- -x objective-c +// bindgen-osx-only + +@interface Foo<__covariant ObjectType> +- (ObjectType)get; +@end + +@interface Bar<__covariant ObjectType>: Foo +- (ObjectType)get; +@end From 13670063ea5aaac6d0cc2cee418cc82c3174aaae Mon Sep 17 00:00:00 2001 From: Sebastian Imlay Date: Thu, 14 May 2020 17:38:14 -0700 Subject: [PATCH 03/15] Removed unneeded methods in tests --- .../tests/objc_category_inheritance.rs | 18 ++----------- .../objc_category_template_inheritance.rs | 27 +++---------------- .../tests/objc_template_inheritance.rs | 18 ++----------- tests/headers/objc_category_inheritance.h | 2 -- .../objc_category_template_inheritance.h | 3 --- tests/headers/objc_template_inheritance.h | 2 -- 6 files changed, 7 insertions(+), 63 deletions(-) diff --git a/tests/expectations/tests/objc_category_inheritance.rs b/tests/expectations/tests/objc_category_inheritance.rs index a3fd0aed25..594bad7aae 100644 --- a/tests/expectations/tests/objc_category_inheritance.rs +++ b/tests/expectations/tests/objc_category_inheritance.rs @@ -28,23 +28,9 @@ impl Foo { } } impl IFoo for Foo {} -pub trait IFoo: Sized + std::ops::Deref { - unsafe fn method(self) - where - ::Target: objc::Message + Sized, - { - msg_send!(self, method) - } -} +pub trait IFoo: Sized + std::ops::Deref {} impl Foo_BarCategory for Foo {} -pub trait Foo_BarCategory: Sized + std::ops::Deref { - unsafe fn categoryMethod(self) - where - ::Target: objc::Message + Sized, - { - msg_send!(self, categoryMethod) - } -} +pub trait Foo_BarCategory: Sized + std::ops::Deref {} #[repr(transparent)] #[derive(Clone, Copy)] pub struct Bar(pub id); diff --git a/tests/expectations/tests/objc_category_template_inheritance.rs b/tests/expectations/tests/objc_category_template_inheritance.rs index f813127dd5..d454c58eaa 100644 --- a/tests/expectations/tests/objc_category_template_inheritance.rs +++ b/tests/expectations/tests/objc_category_template_inheritance.rs @@ -28,23 +28,9 @@ impl Foo { } } impl IFoo for Foo {} -pub trait IFoo: Sized + std::ops::Deref { - unsafe fn get(self) -> u64 - where - ::Target: objc::Message + Sized, - { - msg_send!(self, get) - } -} +pub trait IFoo: Sized + std::ops::Deref {} impl Foo_Baz for Foo {} -pub trait Foo_Baz: Sized + std::ops::Deref { - unsafe fn get(self) -> u64 - where - ::Target: objc::Message + Sized, - { - msg_send!(self, get) - } -} +pub trait Foo_Baz: Sized + std::ops::Deref {} #[repr(transparent)] #[derive(Clone, Copy)] pub struct Bar(pub id); @@ -63,11 +49,4 @@ impl Bar { impl IFoo for Bar {} impl Foo_Baz for Bar {} impl IBar for Bar {} -pub trait IBar: Sized + std::ops::Deref { - unsafe fn get(self) -> u64 - where - ::Target: objc::Message + Sized, - { - msg_send!(self, get) - } -} +pub trait IBar: Sized + std::ops::Deref {} diff --git a/tests/expectations/tests/objc_template_inheritance.rs b/tests/expectations/tests/objc_template_inheritance.rs index 844a3600d9..307873a0e5 100644 --- a/tests/expectations/tests/objc_template_inheritance.rs +++ b/tests/expectations/tests/objc_template_inheritance.rs @@ -28,14 +28,7 @@ impl Foo { } } impl IFoo for Foo {} -pub trait IFoo: Sized + std::ops::Deref { - unsafe fn get(self) -> u64 - where - ::Target: objc::Message + Sized, - { - msg_send!(self, get) - } -} +pub trait IFoo: Sized + std::ops::Deref {} #[repr(transparent)] #[derive(Clone, Copy)] pub struct Bar(pub id); @@ -53,11 +46,4 @@ impl Bar { } impl IFoo for Bar {} impl IBar for Bar {} -pub trait IBar: Sized + std::ops::Deref { - unsafe fn get(self) -> u64 - where - ::Target: objc::Message + Sized, - { - msg_send!(self, get) - } -} +pub trait IBar: Sized + std::ops::Deref {} diff --git a/tests/headers/objc_category_inheritance.h b/tests/headers/objc_category_inheritance.h index 9d31341352..80f294e2ef 100644 --- a/tests/headers/objc_category_inheritance.h +++ b/tests/headers/objc_category_inheritance.h @@ -2,11 +2,9 @@ // bindgen-osx-only @interface Foo --(void)method; @end @interface Foo (BarCategory) --(void)categoryMethod; @end @interface Bar: Foo diff --git a/tests/headers/objc_category_template_inheritance.h b/tests/headers/objc_category_template_inheritance.h index b1c4743406..7c74323966 100644 --- a/tests/headers/objc_category_template_inheritance.h +++ b/tests/headers/objc_category_template_inheritance.h @@ -2,13 +2,10 @@ // bindgen-osx-only @interface Foo<__covariant ObjectType> -- (ObjectType)get; @end @interface Foo<__covariant ObjectType> (Baz) -- (ObjectType)get; @end @interface Bar<__covariant ObjectType>: Foo -- (ObjectType)get; @end diff --git a/tests/headers/objc_template_inheritance.h b/tests/headers/objc_template_inheritance.h index 7580d0c78f..019f571d60 100644 --- a/tests/headers/objc_template_inheritance.h +++ b/tests/headers/objc_template_inheritance.h @@ -2,9 +2,7 @@ // bindgen-osx-only @interface Foo<__covariant ObjectType> -- (ObjectType)get; @end @interface Bar<__covariant ObjectType>: Foo -- (ObjectType)get; @end From 079571d3c3d9161dd6a47275e728fb2c2ab3bfd0 Mon Sep 17 00:00:00 2001 From: Sebastian Imlay Date: Tue, 19 May 2020 08:41:26 -0700 Subject: [PATCH 04/15] A pretty gross implementation :/ --- src/ir/context.rs | 12 +++--------- src/ir/objc.rs | 43 ++++++++++++++++++++++++++----------------- 2 files changed, 29 insertions(+), 26 deletions(-) diff --git a/src/ir/context.rs b/src/ir/context.rs index 7271310e39..346a4a649e 100644 --- a/src/ir/context.rs +++ b/src/ir/context.rs @@ -902,15 +902,9 @@ If you encounter an error missing from this list, please file an issue or a PR!" }) } - /// IterMut over all items that have been defined. - pub fn items_mut(&mut self) -> impl Iterator { - self.items - .iter_mut() - .enumerate() - .filter_map(|(index, item)| { - let item = item.as_mut()?; - Some((ItemId(index), item)) - }) + /// Get a mutable reference to a given ItemId. + pub fn get_item_mut(&mut self, id: ItemId) -> Option<&mut Item> { + self.items.get_mut(id.0)?.as_mut() } /// Have we collected all unresolved type references yet? diff --git a/src/ir/objc.rs b/src/ir/objc.rs index e6b48025d0..7fe70ecc8f 100644 --- a/src/ir/objc.rs +++ b/src/ir/objc.rs @@ -139,6 +139,12 @@ impl ObjCInterface { interface.is_protocol = true; } + // This is the ItemId for the real interface to which a category extends. We must make it + // an optional, set it when we visit the ObjCCategoryDecl and then use it after we've + // visited the entire tree. We must do it in this order to ensure that this interface has + // all the template tags assigned to it. + let mut real_interface_id_for_category: Option = None; + cursor.visit(|c| { match c.kind() { CXCursor_ObjCClassRef => { @@ -148,6 +154,7 @@ impl ObjCInterface { interface.name = c.spelling(); interface.category = Some(cursor.spelling()); + real_interface_id_for_category = Some(Item::from_ty_or_ref(c.cur_type(), c, None, ctx).into()); } } @@ -210,26 +217,28 @@ impl ObjCInterface { interface.rust_name(), needle ); - for (_, item) in ctx.items_mut() { - if let Some(ref mut ty) = item.kind_mut().as_type_mut() { - match ty.kind_mut() { - TypeKind::ObjCInterface(ref mut real_interface) => { - if !real_interface.is_category() { - debug!("Checking interface {}, against needle {:?}", real_interface.name, needle); - if needle == real_interface.name() { - debug!( - "Found real interface {:?}", - real_interface - ); - real_interface.categories.push(( - interface.rust_name(), - interface.template_names.clone(), - )); - break; + + // This is pretty gross but the better way to do this doesn't yield a muutable + // reference. + if let Some(real_interface_id) = real_interface_id_for_category { + if let Some(real_interface_item) = ctx.get_item_mut(real_interface_id) { + if let Some(ty) = real_interface_item.kind().as_type() { + if let TypeKind::ResolvedTypeRef(item_id) = ty.kind() { + let real_interface_id: ItemId = item_id.into(); + let ty = ctx.get_item_mut(real_interface_id).unwrap().kind_mut().as_type_mut().unwrap(); + match ty.kind_mut() { + TypeKind::ObjCInterface(ref mut real_interface) => { + if !real_interface.is_category() { + + real_interface.categories.push(( + interface.rust_name(), + interface.template_names.clone(), + )); + } } + _ => {} } } - _ => {} } } } From 3ad80d9b4b35c3340bd86574438c324f8353277a Mon Sep 17 00:00:00 2001 From: Sebastian Imlay Date: Mon, 25 May 2020 10:17:51 -0700 Subject: [PATCH 05/15] Updated comment --- src/ir/objc.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ir/objc.rs b/src/ir/objc.rs index 7fe70ecc8f..4422625e1e 100644 --- a/src/ir/objc.rs +++ b/src/ir/objc.rs @@ -218,8 +218,7 @@ impl ObjCInterface { needle ); - // This is pretty gross but the better way to do this doesn't yield a muutable - // reference. + // This is pretty gross but using the ItemResolver doesn't yield a mutable reference. if let Some(real_interface_id) = real_interface_id_for_category { if let Some(real_interface_item) = ctx.get_item_mut(real_interface_id) { if let Some(ty) = real_interface_item.kind().as_type() { From 70f38c6d66b4302628b88ea67f3637268b18d419 Mon Sep 17 00:00:00 2001 From: Sebastian Imlay Date: Mon, 25 May 2020 10:19:04 -0700 Subject: [PATCH 06/15] cargo fmt --- src/ir/objc.rs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/ir/objc.rs b/src/ir/objc.rs index 4422625e1e..d1e99b1e5c 100644 --- a/src/ir/objc.rs +++ b/src/ir/objc.rs @@ -220,18 +220,26 @@ impl ObjCInterface { // This is pretty gross but using the ItemResolver doesn't yield a mutable reference. if let Some(real_interface_id) = real_interface_id_for_category { - if let Some(real_interface_item) = ctx.get_item_mut(real_interface_id) { + if let Some(real_interface_item) = + ctx.get_item_mut(real_interface_id) + { if let Some(ty) = real_interface_item.kind().as_type() { if let TypeKind::ResolvedTypeRef(item_id) = ty.kind() { let real_interface_id: ItemId = item_id.into(); - let ty = ctx.get_item_mut(real_interface_id).unwrap().kind_mut().as_type_mut().unwrap(); + let ty = ctx + .get_item_mut(real_interface_id) + .unwrap() + .kind_mut() + .as_type_mut() + .unwrap(); match ty.kind_mut() { - TypeKind::ObjCInterface(ref mut real_interface) => { + TypeKind::ObjCInterface( + ref mut real_interface, + ) => { if !real_interface.is_category() { - real_interface.categories.push(( - interface.rust_name(), - interface.template_names.clone(), + interface.rust_name(), + interface.template_names.clone(), )); } } From 7a642c36410151f5be2d4513ff4033fa5e98c988 Mon Sep 17 00:00:00 2001 From: Sebastian Imlay Date: Tue, 26 May 2020 17:21:37 -0700 Subject: [PATCH 07/15] Added get_parent_ty to clean up the option branches --- src/ir/objc.rs | 61 ++++++++++++++++++++++++++------------------------ 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/src/ir/objc.rs b/src/ir/objc.rs index d1e99b1e5c..28a725c474 100644 --- a/src/ir/objc.rs +++ b/src/ir/objc.rs @@ -4,7 +4,7 @@ use super::context::{BindgenContext, ItemId}; use super::function::FunctionSig; use super::item::Item; use super::traversal::{Trace, Tracer}; -use super::ty::TypeKind; +use super::ty::{Type, TypeKind}; use crate::clang; use crate::parse::ClangItemParser; use clang_sys::CXChildVisit_Continue; @@ -217,42 +217,45 @@ impl ObjCInterface { interface.rust_name(), needle ); - - // This is pretty gross but using the ItemResolver doesn't yield a mutable reference. - if let Some(real_interface_id) = real_interface_id_for_category { - if let Some(real_interface_item) = - ctx.get_item_mut(real_interface_id) - { - if let Some(ty) = real_interface_item.kind().as_type() { - if let TypeKind::ResolvedTypeRef(item_id) = ty.kind() { - let real_interface_id: ItemId = item_id.into(); - let ty = ctx - .get_item_mut(real_interface_id) - .unwrap() - .kind_mut() - .as_type_mut() - .unwrap(); - match ty.kind_mut() { - TypeKind::ObjCInterface( - ref mut real_interface, - ) => { - if !real_interface.is_category() { - real_interface.categories.push(( - interface.rust_name(), - interface.template_names.clone(), - )); - } - } - _ => {} - } + if let Some(ref mut ty) = + Self::get_parent_ty(ctx, real_interface_id_for_category) + { + match ty.kind_mut() { + TypeKind::ObjCInterface(ref mut real_interface) => { + if !real_interface.is_category() { + real_interface.categories.push(( + interface.rust_name(), + interface.template_names.clone(), + )); } } + _ => {} } } } Some(interface) } + fn get_parent_ty( + ctx: &mut BindgenContext, + parent_id: Option, + ) -> Option<&mut Type> { + // This is pretty gross but using the ItemResolver doesn't yield a mutable reference. + let real_interface_item = ctx.get_item_mut(parent_id?)?; + let ty = real_interface_item.kind().as_type()?; + + if let TypeKind::ResolvedTypeRef(item_id) = ty.kind() { + let real_interface_id: ItemId = item_id.into(); + let ty = ctx + .get_item_mut(real_interface_id)? + .kind_mut() + .as_type_mut()?; + return Some(ty); + } else { + return None; + } + } + fn add_method(&mut self, method: ObjCMethod) { if method.is_class_method { self.class_methods.push(method); From 996af2e09a4e7b691eefec2a13118afe30ca7c3b Mon Sep 17 00:00:00 2001 From: Sebastian Imlay Date: Mon, 3 Aug 2020 23:59:25 -0700 Subject: [PATCH 08/15] Remove autogenerated comment --- tests/expectations/tests/objc_category_inheritance.rs | 2 -- tests/expectations/tests/objc_category_template_inheritance.rs | 2 -- tests/expectations/tests/objc_template_inheritance.rs | 2 -- 3 files changed, 6 deletions(-) diff --git a/tests/expectations/tests/objc_category_inheritance.rs b/tests/expectations/tests/objc_category_inheritance.rs index 594bad7aae..414e2076a1 100644 --- a/tests/expectations/tests/objc_category_inheritance.rs +++ b/tests/expectations/tests/objc_category_inheritance.rs @@ -1,5 +1,3 @@ -/* automatically generated by rust-bindgen */ - #![allow( dead_code, non_snake_case, diff --git a/tests/expectations/tests/objc_category_template_inheritance.rs b/tests/expectations/tests/objc_category_template_inheritance.rs index d454c58eaa..36c9168fd4 100644 --- a/tests/expectations/tests/objc_category_template_inheritance.rs +++ b/tests/expectations/tests/objc_category_template_inheritance.rs @@ -1,5 +1,3 @@ -/* automatically generated by rust-bindgen */ - #![allow( dead_code, non_snake_case, diff --git a/tests/expectations/tests/objc_template_inheritance.rs b/tests/expectations/tests/objc_template_inheritance.rs index 307873a0e5..aec2fdfc8b 100644 --- a/tests/expectations/tests/objc_template_inheritance.rs +++ b/tests/expectations/tests/objc_template_inheritance.rs @@ -1,5 +1,3 @@ -/* automatically generated by rust-bindgen */ - #![allow( dead_code, non_snake_case, From a2287a347a7259706b6de33ed1c470605fa1256b Mon Sep 17 00:00:00 2001 From: Sebastian Imlay Date: Fri, 21 Aug 2020 11:33:28 -0700 Subject: [PATCH 09/15] Added fixed stuff from minor nits --- src/ir/objc.rs | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/src/ir/objc.rs b/src/ir/objc.rs index 28a725c474..915d57b001 100644 --- a/src/ir/objc.rs +++ b/src/ir/objc.rs @@ -211,7 +211,7 @@ impl ObjCInterface { if interface.is_category() { // If this interface is a category, we need to find the interface that this category // extends. - let needle = format!("{}", interface.name()); + let needle = interface.name(); debug!( "Category {} belongs to {}, find the item", interface.rust_name(), @@ -220,16 +220,13 @@ impl ObjCInterface { if let Some(ref mut ty) = Self::get_parent_ty(ctx, real_interface_id_for_category) { - match ty.kind_mut() { - TypeKind::ObjCInterface(ref mut real_interface) => { - if !real_interface.is_category() { - real_interface.categories.push(( + if let TypeKind::ObjCInterface(ref mut real_interface) = ty.kind_mut() { + if !real_interface.is_category() { + real_interface.categories.push(( interface.rust_name(), interface.template_names.clone(), - )); - } + )); } - _ => {} } } } @@ -243,17 +240,17 @@ impl ObjCInterface { // This is pretty gross but using the ItemResolver doesn't yield a mutable reference. let real_interface_item = ctx.get_item_mut(parent_id?)?; let ty = real_interface_item.kind().as_type()?; - - if let TypeKind::ResolvedTypeRef(item_id) = ty.kind() { - let real_interface_id: ItemId = item_id.into(); - let ty = ctx - .get_item_mut(real_interface_id)? - .kind_mut() - .as_type_mut()?; - return Some(ty); - } else { - return None; - } + let item_id = match ty.kind() { + TypeKind::ResolvedTypeRef(item_id) => item_id, + _ => return None, + }; + + let real_interface_id: ItemId = item_id.into(); + let ty = ctx + .get_item_mut(real_interface_id)? + .kind_mut() + .as_type_mut()?; + return Some(ty); } fn add_method(&mut self, method: ObjCMethod) { From b9615e65f82ba9f9309340736fe9ab15bee125e7 Mon Sep 17 00:00:00 2001 From: Sebastian Imlay Date: Mon, 24 Aug 2020 12:59:22 -0700 Subject: [PATCH 10/15] fix for rustfmt --- src/ir/objc.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/ir/objc.rs b/src/ir/objc.rs index 915d57b001..c9749bd06c 100644 --- a/src/ir/objc.rs +++ b/src/ir/objc.rs @@ -220,11 +220,13 @@ impl ObjCInterface { if let Some(ref mut ty) = Self::get_parent_ty(ctx, real_interface_id_for_category) { - if let TypeKind::ObjCInterface(ref mut real_interface) = ty.kind_mut() { + if let TypeKind::ObjCInterface(ref mut real_interface) = + ty.kind_mut() + { if !real_interface.is_category() { real_interface.categories.push(( - interface.rust_name(), - interface.template_names.clone(), + interface.rust_name(), + interface.template_names.clone(), )); } } From 84a3de91cf29ae398af5bbe012fd8eaa830d01d0 Mon Sep 17 00:00:00 2001 From: Sebastian Imlay Date: Mon, 31 Aug 2020 22:22:20 -0700 Subject: [PATCH 11/15] Added loop for resolving typerefs. --- src/ir/objc.rs | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/src/ir/objc.rs b/src/ir/objc.rs index c9749bd06c..7e47eb201f 100644 --- a/src/ir/objc.rs +++ b/src/ir/objc.rs @@ -1,6 +1,6 @@ //! Objective C types -use super::context::{BindgenContext, ItemId}; +use super::context::{BindgenContext, ItemId, TypeId}; use super::function::FunctionSig; use super::item::Item; use super::traversal::{Trace, Tracer}; @@ -211,12 +211,6 @@ impl ObjCInterface { if interface.is_category() { // If this interface is a category, we need to find the interface that this category // extends. - let needle = interface.name(); - debug!( - "Category {} belongs to {}, find the item", - interface.rust_name(), - needle - ); if let Some(ref mut ty) = Self::get_parent_ty(ctx, real_interface_id_for_category) { @@ -240,14 +234,27 @@ impl ObjCInterface { parent_id: Option, ) -> Option<&mut Type> { // This is pretty gross but using the ItemResolver doesn't yield a mutable reference. - let real_interface_item = ctx.get_item_mut(parent_id?)?; - let ty = real_interface_item.kind().as_type()?; - let item_id = match ty.kind() { - TypeKind::ResolvedTypeRef(item_id) => item_id, - _ => return None, - }; - - let real_interface_id: ItemId = item_id.into(); + let mut ty = ctx.resolve_item_fallible(parent_id?)?.kind().as_type()?; + let mut item_id: Option<&TypeId> = None; + loop { + match ty.kind() { + TypeKind::ResolvedTypeRef(ref_id) => { + let ref_item: ItemId = ref_id.into(); + ty = ctx + .resolve_item_fallible(ref_item)? + .kind() + .as_type()?; + //ty = ref_item.kind().as_type()()?;; + item_id = Some(ref_id); + } + TypeKind::ObjCInterface(..) => { + break; + } + _ => return None, + }; + } + + let real_interface_id: ItemId = item_id?.into(); let ty = ctx .get_item_mut(real_interface_id)? .kind_mut() From 0ce8926f3d4159aa83bb5784ab69f6517988f19a Mon Sep 17 00:00:00 2001 From: Sebastian Imlay Date: Wed, 16 Sep 2020 17:46:42 -0700 Subject: [PATCH 12/15] Fixes from merging master into feature branch --- src/codegen/mod.rs | 44 +++++++++---------- .../tests/objc_category_inheritance.rs | 21 ++++++++- .../objc_category_template_inheritance.rs | 4 +- .../tests/objc_template_inheritance.rs | 4 +- 4 files changed, 45 insertions(+), 28 deletions(-) diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index 739c58ab1b..cb73cea3d2 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -3980,6 +3980,28 @@ impl CodeGenerator for ObjCInterface { result.push(impl_trait); } } + for (category_name, category_template_names) in + &parent.categories + { + let category_name = ctx.rust_ident(category_name); + let impl_trait = if !category_template_names.is_empty() + { + let template_names: Vec = + category_template_names + .iter() + .map(|g| ctx.rust_ident(g)) + .collect(); + quote! { + impl <#(#template_names :'static),*> #category_name <#(#template_names),*> for #class_name { + } + } + } else { + quote! { + impl #category_name for #class_name { } + } + }; + result.push(impl_trait); + } if !parent.is_template() { let parent_struct_name = parent.name(); let child_struct_name = self.name(); @@ -4011,28 +4033,6 @@ impl CodeGenerator for ObjCInterface { } }; result.push(try_into_block); - for (category_name, category_template_names) in - &parent.categories - { - let category_name = ctx.rust_ident(category_name); - let impl_trait = if !category_template_names.is_empty() - { - let template_names: Vec = - category_template_names - .iter() - .map(|g| ctx.rust_ident(g)) - .collect(); - quote! { - impl <#(#template_names :'static),*> #category_name <#(#template_names),*> for #class_name { - } - } - } else { - quote! { - impl #category_name for #class_name { } - } - }; - result.push(impl_trait); - } parent.parent_class } else { None diff --git a/tests/expectations/tests/objc_category_inheritance.rs b/tests/expectations/tests/objc_category_inheritance.rs index 414e2076a1..a1a33eb128 100644 --- a/tests/expectations/tests/objc_category_inheritance.rs +++ b/tests/expectations/tests/objc_category_inheritance.rs @@ -11,7 +11,7 @@ extern crate objc; #[allow(non_camel_case_types)] pub type id = *mut objc::runtime::Object; #[repr(transparent)] -#[derive(Clone, Copy)] +#[derive(Clone)] pub struct Foo(pub id); impl std::ops::Deref for Foo { type Target = objc::runtime::Object; @@ -30,7 +30,7 @@ pub trait IFoo: Sized + std::ops::Deref {} impl Foo_BarCategory for Foo {} pub trait Foo_BarCategory: Sized + std::ops::Deref {} #[repr(transparent)] -#[derive(Clone, Copy)] +#[derive(Clone)] pub struct Bar(pub id); impl std::ops::Deref for Bar { type Target = objc::runtime::Object; @@ -46,5 +46,22 @@ impl Bar { } impl IFoo for Bar {} impl Foo_BarCategory for Bar {} +impl From for Foo { + fn from(child: Bar) -> Foo { + Foo(child.0) + } +} +impl std::convert::TryFrom for Bar { + type Error = &'static str; + fn try_from(parent: Foo) -> Result { + let is_kind_of: bool = + unsafe { msg_send!(parent, isKindOfClass: class!(Bar)) }; + if is_kind_of { + Ok(Bar(parent.0)) + } else { + Err("This Foo cannot be downcasted to Bar") + } + } +} impl IBar for Bar {} pub trait IBar: Sized + std::ops::Deref {} diff --git a/tests/expectations/tests/objc_category_template_inheritance.rs b/tests/expectations/tests/objc_category_template_inheritance.rs index 36c9168fd4..1b762603eb 100644 --- a/tests/expectations/tests/objc_category_template_inheritance.rs +++ b/tests/expectations/tests/objc_category_template_inheritance.rs @@ -11,7 +11,7 @@ extern crate objc; #[allow(non_camel_case_types)] pub type id = *mut objc::runtime::Object; #[repr(transparent)] -#[derive(Clone, Copy)] +#[derive(Clone)] pub struct Foo(pub id); impl std::ops::Deref for Foo { type Target = objc::runtime::Object; @@ -30,7 +30,7 @@ pub trait IFoo: Sized + std::ops::Deref {} impl Foo_Baz for Foo {} pub trait Foo_Baz: Sized + std::ops::Deref {} #[repr(transparent)] -#[derive(Clone, Copy)] +#[derive(Clone)] pub struct Bar(pub id); impl std::ops::Deref for Bar { type Target = objc::runtime::Object; diff --git a/tests/expectations/tests/objc_template_inheritance.rs b/tests/expectations/tests/objc_template_inheritance.rs index aec2fdfc8b..13629bdedd 100644 --- a/tests/expectations/tests/objc_template_inheritance.rs +++ b/tests/expectations/tests/objc_template_inheritance.rs @@ -11,7 +11,7 @@ extern crate objc; #[allow(non_camel_case_types)] pub type id = *mut objc::runtime::Object; #[repr(transparent)] -#[derive(Clone, Copy)] +#[derive(Clone)] pub struct Foo(pub id); impl std::ops::Deref for Foo { type Target = objc::runtime::Object; @@ -28,7 +28,7 @@ impl Foo { impl IFoo for Foo {} pub trait IFoo: Sized + std::ops::Deref {} #[repr(transparent)] -#[derive(Clone, Copy)] +#[derive(Clone)] pub struct Bar(pub id); impl std::ops::Deref for Bar { type Target = objc::runtime::Object; From 78bf0911a06707518e55e63fdae6860d00ba735f Mon Sep 17 00:00:00 2001 From: Sebastian Imlay Date: Wed, 16 Sep 2020 20:47:18 -0700 Subject: [PATCH 13/15] rustfmt and updated changelog --- CHANGELOG.md | 2 ++ src/codegen/mod.rs | 31 +++++++++++++++---------------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 487405ee8b..c25fdf8cf7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -121,6 +121,7 @@ Released YYYY/MM/DD ## Added * Objective-c bindings generate `From for ParentClass` as well as `TryFrom for ChildClass` ([#1883][]). +* Objective-c bindings generate impl blocks for categories inherited through parent classes ([#1784][]). ## Changed @@ -144,6 +145,7 @@ Released YYYY/MM/DD [#1883]: https://github.com/rust-lang/rust-bindgen/issues/1883 +[#1784]: https://github.com/rust-lang/rust-bindgen/issues/1784 -------------------------------------------------------------------------------- diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index cb73cea3d2..680e024457 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -3982,26 +3982,25 @@ impl CodeGenerator for ObjCInterface { } for (category_name, category_template_names) in &parent.categories - { - let category_name = ctx.rust_ident(category_name); - let impl_trait = if !category_template_names.is_empty() - { - let template_names: Vec = - category_template_names + { + let category_name = ctx.rust_ident(category_name); + let impl_trait = if !category_template_names.is_empty() { + let template_names: Vec = + category_template_names .iter() .map(|g| ctx.rust_ident(g)) .collect(); - quote! { - impl <#(#template_names :'static),*> #category_name <#(#template_names),*> for #class_name { - } - } - } else { - quote! { - impl #category_name for #class_name { } + quote! { + impl <#(#template_names :'static),*> #category_name <#(#template_names),*> for #class_name { } - }; - result.push(impl_trait); - } + } + } else { + quote! { + impl #category_name for #class_name { } + } + }; + result.push(impl_trait); + } if !parent.is_template() { let parent_struct_name = parent.name(); let child_struct_name = self.name(); From bd5f9195a86332cf7abde6cd87b68fc377db9bfb Mon Sep 17 00:00:00 2001 From: Sebastian Imlay Date: Thu, 23 Dec 2021 15:27:50 -0500 Subject: [PATCH 14/15] maybe fix cargo fmt --- src/ir/objc.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ir/objc.rs b/src/ir/objc.rs index fe38905dc1..be56b7dada 100644 --- a/src/ir/objc.rs +++ b/src/ir/objc.rs @@ -152,7 +152,10 @@ impl ObjCInterface { interface.name = c.spelling(); interface.category = Some(cursor.spelling()); - real_interface_id_for_category = Some(Item::from_ty_or_ref(c.cur_type(), c, None, ctx).into()); + real_interface_id_for_category = Some( + Item::from_ty_or_ref(c.cur_type(), c, None, ctx) + .into(), + ); } } From 4c00a974646a06fb02d6501748bc0ddcdfae7b0f Mon Sep 17 00:00:00 2001 From: Sebastian Imlay Date: Thu, 23 Dec 2021 15:28:58 -0500 Subject: [PATCH 15/15] fix cargo fmt --- src/ir/objc.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ir/objc.rs b/src/ir/objc.rs index be56b7dada..8fd2cc32fc 100644 --- a/src/ir/objc.rs +++ b/src/ir/objc.rs @@ -156,7 +156,6 @@ impl ObjCInterface { Item::from_ty_or_ref(c.cur_type(), c, None, ctx) .into(), ); - } } CXCursor_ObjCProtocolRef => {