From 8216c05940dfac46479245e6e91268f3e0e6368a Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Tue, 1 Nov 2022 14:38:20 +0100 Subject: [PATCH 1/3] Add `#[method_id(...)]` attribute to `extern_methods!` --- objc2/CHANGELOG.md | 1 + objc2/src/macros/declare_class.rs | 17 +++++ objc2/src/macros/extern_class.rs | 62 +++++++++++++++- objc2/src/macros/extern_methods.rs | 72 ++++++++++-------- test-ui/ui/extern_methods_invalid_type.rs | 34 +++++++++ test-ui/ui/extern_methods_invalid_type.stderr | 74 +++++++++++++++++++ .../ui/extern_methods_missing_method.stderr | 2 +- test-ui/ui/extern_methods_selector_twice.rs | 28 +++++++ .../ui/extern_methods_selector_twice.stderr | 27 +++++++ 9 files changed, 280 insertions(+), 37 deletions(-) create mode 100644 test-ui/ui/extern_methods_invalid_type.rs create mode 100644 test-ui/ui/extern_methods_invalid_type.stderr create mode 100644 test-ui/ui/extern_methods_selector_twice.rs create mode 100644 test-ui/ui/extern_methods_selector_twice.stderr diff --git a/objc2/CHANGELOG.md b/objc2/CHANGELOG.md index 0233ac48f..e5929ffaa 100644 --- a/objc2/CHANGELOG.md +++ b/objc2/CHANGELOG.md @@ -30,6 +30,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). * Added the ability to specify an extra parameter at the end of the selector in methods declared with `extern_methods!`, and let that be the `NSError**` parameter. +* Added `#[method_id(...)]` attribute to `extern_methods!`. ### Changed * Allow other types than `&Class` as the receiver in `msg_send_id!` methods diff --git a/objc2/src/macros/declare_class.rs b/objc2/src/macros/declare_class.rs index b81c38c7c..6eced7872 100644 --- a/objc2/src/macros/declare_class.rs +++ b/objc2/src/macros/declare_class.rs @@ -828,6 +828,10 @@ macro_rules! __declare_class_register_out { ($crate::__declare_class_register_out) ($(#[$($m)*])*) @call_sel + + // Will add + // @(sel) + // @(output macro) }, Self::$name as $crate::__fn_ptr! { @($($qualifiers)*) $($args_start)* $($args_rest)* @@ -853,6 +857,10 @@ macro_rules! __declare_class_register_out { ($crate::__declare_class_register_out) ($(#[$($m)*])*) @call_sel + + // Will add + // @(sel) + // @(output macro) }, Self::$name as $crate::__fn_ptr! { @($($qualifiers)*) $($args_start)* $($args_rest)* @@ -863,9 +871,18 @@ macro_rules! __declare_class_register_out { { @call_sel @($($sel:tt)*) + @(msg_send) } => { $crate::sel!($($sel)*) }; + + { + @call_sel + @($($sel:tt)*) + @(msg_send_id) + } => { + compile_error!("`#[method_id(...)]` is not supported in `declare_class!` yet"); + }; } /// Create function pointer type with inferred arguments. diff --git a/objc2/src/macros/extern_class.rs b/objc2/src/macros/extern_class.rs index 097b32d20..21e0135d2 100644 --- a/objc2/src/macros/extern_class.rs +++ b/objc2/src/macros/extern_class.rs @@ -397,7 +397,9 @@ macro_rules! __inner_extern_class { #[macro_export] macro_rules! __attribute_helper { // Convert a set of attributes described with `@[...]` to `#[...]`, while - // parsing out the `method(...)` attribute. + // parsing out the `method(...)` or `method_id(...)` attribute. + + // method { @strip_sel @[method($($_sel_args:tt)*)] @@ -414,6 +416,24 @@ macro_rules! __attribute_helper { ($($fn)*) } }; + // method_id + { + @strip_sel + @[method_id($($_sel_args:tt)*)] + $(@[$($m_rest:tt)*])* + + $(#[$($m:tt)*])* + ($($fn:tt)*) + } => { + $crate::__attribute_helper! { + @strip_sel + $(@[$($m_rest)*])* + + $(#[$($m)*])* + ($($fn)*) + } + }; + // Others { @strip_sel @[$($m_checked:tt)*] @@ -431,6 +451,7 @@ macro_rules! __attribute_helper { ($($fn)*) } }; + // Final output { @strip_sel $(#[$($m:tt)*])* @@ -440,7 +461,8 @@ macro_rules! __attribute_helper { $($fn)* }; - // Extract the `#[method(...)]` attribute and send it to another macro + // Extract the `#[method(...)]` and `#[method_id(...)]` attribute and send + // it to another macro { @extract_sel ($out_macro:path) @@ -455,8 +477,29 @@ macro_rules! __attribute_helper { ($($rest)*) $out_macro!( $($macro_args)* - // Append selector to the end of the macro arguments + // Append selector and macro name to the end of the macro arguments @($($sel)*) + @(msg_send) + ) + } + }; + { + @extract_sel + ($out_macro:path) + ( + #[method_id($($sel:tt)*)] + $($rest:tt)* + ) + $($macro_args:tt)* + } => { + $crate::__attribute_helper! { + @extract_sel_duplicate + ($($rest)*) + $out_macro!( + $($macro_args)* + + @($($sel)*) + @(msg_send_id) ) } }; @@ -482,9 +525,10 @@ macro_rules! __attribute_helper { () $($macro_args:tt)* } => {{ - compile_error!("Must specify the desired selector using `#[method(...)]`"); + compile_error!("Must specify the desired selector using `#[method(...)]` or `#[method_id(...)]`"); }}; + // Duplicate checking { @extract_sel_duplicate ( @@ -495,6 +539,16 @@ macro_rules! __attribute_helper { } => {{ compile_error!("Cannot not specify a selector twice!"); }}; + { + @extract_sel_duplicate + ( + #[method_id($($_sel_args:tt)*)] + $($rest:tt)* + ) + $($output:tt)* + } => {{ + compile_error!("Cannot not specify a selector twice!"); + }}; { @extract_sel_duplicate ( diff --git a/objc2/src/macros/extern_methods.rs b/objc2/src/macros/extern_methods.rs index c2dff4469..cc5afa29a 100644 --- a/objc2/src/macros/extern_methods.rs +++ b/objc2/src/macros/extern_methods.rs @@ -1,7 +1,10 @@ /// Define methods on an external class. /// /// This is a convenience macro to easily generate associated functions and -/// methods that call [`msg_send!`][crate::msg_send] appropriately. +/// methods that call [`msg_send!`] or [`msg_send_id!`] appropriately. +/// +/// [`msg_send!`]: crate::msg_send +/// [`msg_send_id!`]: crate::msg_send_id /// /// /// # Specification @@ -14,8 +17,11 @@ /// instance method, and if you don't it will be assumed to be a class method. /// /// The desired selector can be specified using the `#[method(my:selector:)]` -/// attribute. The name of the function doesn't matter for out purposes, but -/// is of course what the user will use to access the functionality. +/// or `#[method_id(my:selector:)]` attribute. The `method` attribute maps to +/// a call to [`msg_send!`], while the `method_id` maps to [`msg_send_id!`]. +/// +/// The name of the function doesn't matter for out purposes, but is of course +/// what the user will use to access the functionality. /// /// If you specify a function/method with a body, the macro will simply ignore /// it. @@ -27,8 +33,12 @@ /// # Safety /// /// You must ensure that any methods you declare with the `#[method(...)]` -/// attribute upholds the safety guarantees decribed in the -/// [`msg_send!`][crate::msg_send] macro, _or_ are marked `unsafe`. +/// attribute upholds the safety guarantees decribed in the [`msg_send!`] +/// macro, _or_ are marked `unsafe`. +/// +/// Likewise, you must ensure that any methods you declare with the +/// `#[method_id(...)]` attribute upholds the safety guarantees decribed in +/// the [`msg_send_id!`] macro, _or_ are marked `unsafe`. /// /// /// # Examples @@ -39,7 +49,7 @@ /// /// ``` /// use objc2::foundation::{NSError, NSObject, NSRange, NSString, NSUInteger}; -/// use objc2::rc::{Id, Shared}; +/// use objc2::rc::{Allocated, Id, Shared}; /// use objc2::runtime::Object; /// use objc2::{extern_class, extern_methods, msg_send_id, Encode, Encoding, ClassType}; /// # @@ -71,20 +81,15 @@ /// /// extern_methods!( /// /// Creation methods. -/// // TODO: Support methods returning `Id` /// unsafe impl NSCalendar { -/// pub fn current() -> Id { -/// unsafe { msg_send_id![Self::class(), currentCalendar] } -/// } -/// -/// pub fn new(identifier: &NSCalendarIdentifier) -> Id { -/// unsafe { -/// msg_send_id![ -/// Self::alloc(), -/// initWithCalendarIdentifier: identifier, -/// ] -/// } -/// } +/// #[method_id(currentCalendar)] +/// pub fn current() -> Id; +/// +/// #[method_id(initWithCalendarIdentifier:)] +/// pub fn init( +/// this: Option>, +/// identifier: &NSCalendarIdentifier, +/// ) -> Id; /// } /// /// /// Accessor methods. @@ -93,9 +98,8 @@ /// #[method(firstWeekday)] /// pub fn first_weekday(&self) -> NSUInteger; /// -/// pub fn am_symbol(&self) -> Id { -/// unsafe { msg_send_id![self, amSymbol] } -/// } +/// #[method_id(amSymbol)] +/// pub fn am_symbol(&self) -> Id; /// /// #[method(date:matchesComponents:)] /// // `unsafe` because we don't have definitions for `NSDate` and @@ -129,7 +133,7 @@ /// /// ``` /// # use objc2::foundation::{NSError, NSObject, NSRange, NSString, NSUInteger}; -/// # use objc2::rc::{Id, Shared}; +/// # use objc2::rc::{Allocated, Id, Shared}; /// # use objc2::runtime::Object; /// # use objc2::{extern_class, extern_methods, msg_send_id, Encode, Encoding, ClassType}; /// # @@ -166,13 +170,11 @@ /// unsafe { msg_send_id![Self::class(), currentCalendar] } /// } /// -/// pub fn new(identifier: &NSCalendarIdentifier) -> Id { -/// unsafe { -/// msg_send_id![ -/// Self::alloc(), -/// initWithCalendarIdentifier: identifier, -/// ] -/// } +/// pub fn init( +/// this: Option>, +/// identifier: &NSCalendarIdentifier, +/// ) -> Id { +/// unsafe { msg_send_id![this, initWithCalendarIdentifier: identifier] } /// } /// } /// @@ -334,6 +336,10 @@ macro_rules! __inner_extern_methods { @($($kind)*) @($($args_start)*) @($($args_rest)*) + + // Will add + // @(sel) + // @(output macro) } } }) @@ -349,9 +355,10 @@ macro_rules! __inner_extern_methods { ) @($($args_rest:tt)*) @($($sel:tt)*) + @($macro:ident) } => { $crate::__collect_msg_send! { - $crate::msg_send; + $crate::$macro; $self_or_this; ($($sel)*); ($($args_rest)*); @@ -366,9 +373,10 @@ macro_rules! __inner_extern_methods { ) @($($args_rest:tt)*) @($($sel:tt)*) + @($macro:ident) } => { $crate::__collect_msg_send! { - $crate::msg_send; + $crate::$macro; Self::class(); ($($sel)*); ($($args_rest)*); diff --git a/test-ui/ui/extern_methods_invalid_type.rs b/test-ui/ui/extern_methods_invalid_type.rs new file mode 100644 index 000000000..6c12b81e3 --- /dev/null +++ b/test-ui/ui/extern_methods_invalid_type.rs @@ -0,0 +1,34 @@ +use objc2::{extern_class, extern_methods, ClassType}; +use objc2::foundation::NSObject; +use objc2::rc::{Id, Owned}; + +extern_class!( + pub struct MyObject; + + unsafe impl ClassType for MyObject { + type Super = NSObject; + } +); + +extern_methods!( + unsafe impl MyObject { + #[method(a)] + fn a(&self) -> Id; + } +); + +extern_methods!( + unsafe impl MyObject { + #[method_id(b)] + fn b(&self) -> i32; + } +); + +extern_methods!( + unsafe impl MyObject { + #[method_id(init)] + fn init(&mut self) -> Option>; + } +); + +fn main() {} diff --git a/test-ui/ui/extern_methods_invalid_type.stderr b/test-ui/ui/extern_methods_invalid_type.stderr new file mode 100644 index 000000000..1b63fa2bc --- /dev/null +++ b/test-ui/ui/extern_methods_invalid_type.stderr @@ -0,0 +1,74 @@ +error[E0277]: the trait bound `Id: Encode` is not satisfied + --> ui/extern_methods_invalid_type.rs + | + | / extern_methods!( + | | unsafe impl MyObject { + | | #[method(a)] + | | fn a(&self) -> Id; + | | } + | | ); + | |_^ the trait `Encode` is not implemented for `Id` + | + = help: the following other types implement trait `Encode`: + &'a T + &'a mut T + () + *const T + *const c_void + *mut T + *mut c_void + AtomicI16 + and $N others + = note: required for `Id` to implement `EncodeConvert` +note: required by a bound in `send_message` + --> $WORKSPACE/objc2/src/message/mod.rs + | + | R: EncodeConvert, + | ^^^^^^^^^^^^^ required by this bound in `send_message` + = note: this error originates in the macro `$crate::__msg_send_helper` which comes from the expansion of the macro `extern_methods` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `i32: MaybeUnwrap` is not satisfied + --> ui/extern_methods_invalid_type.rs + | + | / extern_methods!( + | | unsafe impl MyObject { + | | #[method_id(b)] + | | fn b(&self) -> i32; + | | } + | | ); + | |_^ the trait `MaybeUnwrap` is not implemented for `i32` + | + = help: the following other types implement trait `MaybeUnwrap`: + Allocated + Id + Option> + Option> +note: required by a bound in `send_message_id` + --> $WORKSPACE/objc2/src/__macro_helpers.rs + | + | unsafe fn send_message_id>( + | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `send_message_id` + = note: this error originates in the macro `$crate::__msg_send_id_helper` which comes from the expansion of the macro `extern_methods` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> ui/extern_methods_invalid_type.rs + | + | / extern_methods!( + | | unsafe impl MyObject { + | | #[method_id(init)] + | | fn init(&mut self) -> Option>; + | | } + | | ); + | | ^ + | | | + | |_expected enum `Option`, found `&mut MyObject` + | arguments to this function are incorrect + | + = note: expected enum `Option>` + found mutable reference `&mut MyObject` +note: associated function defined here + --> $WORKSPACE/objc2/src/__macro_helpers.rs + | + | unsafe fn send_message_id>( + | ^^^^^^^^^^^^^^^ + = note: this error originates in the macro `$crate::__inner_extern_methods` which comes from the expansion of the macro `extern_methods` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/test-ui/ui/extern_methods_missing_method.stderr b/test-ui/ui/extern_methods_missing_method.stderr index a5bd9bfb8..ddb89e188 100644 --- a/test-ui/ui/extern_methods_missing_method.stderr +++ b/test-ui/ui/extern_methods_missing_method.stderr @@ -1,4 +1,4 @@ -error: Must specify the desired selector using `#[method(...)]` +error: Must specify the desired selector using `#[method(...)]` or `#[method_id(...)]` --> ui/extern_methods_missing_method.rs | | / extern_methods!( diff --git a/test-ui/ui/extern_methods_selector_twice.rs b/test-ui/ui/extern_methods_selector_twice.rs new file mode 100644 index 000000000..7188111cc --- /dev/null +++ b/test-ui/ui/extern_methods_selector_twice.rs @@ -0,0 +1,28 @@ +use objc2::{extern_class, extern_methods, ClassType}; +use objc2::foundation::NSObject; + +extern_class!( + pub struct MyObject; + + unsafe impl ClassType for MyObject { + type Super = NSObject; + } +); + +extern_methods!( + unsafe impl MyObject { + #[method(a)] + #[method(a)] + fn a(); + } +); + +extern_methods!( + unsafe impl MyObject { + #[method(b)] + #[method_id(b)] + fn b(); + } +); + +fn main() {} diff --git a/test-ui/ui/extern_methods_selector_twice.stderr b/test-ui/ui/extern_methods_selector_twice.stderr new file mode 100644 index 000000000..491025027 --- /dev/null +++ b/test-ui/ui/extern_methods_selector_twice.stderr @@ -0,0 +1,27 @@ +error: Cannot not specify a selector twice! + --> ui/extern_methods_selector_twice.rs + | + | / extern_methods!( + | | unsafe impl MyObject { + | | #[method(a)] + | | #[method(a)] + | | fn a(); + | | } + | | ); + | |_^ + | + = note: this error originates in the macro `$crate::__attribute_helper` which comes from the expansion of the macro `extern_methods` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: Cannot not specify a selector twice! + --> ui/extern_methods_selector_twice.rs + | + | / extern_methods!( + | | unsafe impl MyObject { + | | #[method(b)] + | | #[method_id(b)] + | | fn b(); + | | } + | | ); + | |_^ + | + = note: this error originates in the macro `$crate::__attribute_helper` which comes from the expansion of the macro `extern_methods` (in Nightly builds, run with -Z macro-backtrace for more info) From b253482fba42036b955644b0c1ae4a6129af5eeb Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Tue, 1 Nov 2022 14:39:38 +0100 Subject: [PATCH 2/3] Fix duplicate selector extraction --- objc2/CHANGELOG.md | 3 +++ objc2/src/macros/extern_class.rs | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/objc2/CHANGELOG.md b/objc2/CHANGELOG.md index e5929ffaa..c11af9856 100644 --- a/objc2/CHANGELOG.md +++ b/objc2/CHANGELOG.md @@ -46,6 +46,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). associated functions whoose first parameter is called `this`, is treated as instance methods instead of class methods. +### Fixed +* Fixed duplicate selector extraction in `extern_methods!`. + ## 0.3.0-beta.3 - 2022-09-01 diff --git a/objc2/src/macros/extern_class.rs b/objc2/src/macros/extern_class.rs index 21e0135d2..0a39a51ac 100644 --- a/objc2/src/macros/extern_class.rs +++ b/objc2/src/macros/extern_class.rs @@ -559,7 +559,7 @@ macro_rules! __attribute_helper { } => { $crate::__attribute_helper! { @extract_sel_duplicate - ($($rest:tt)*) + ($($rest)*) $($output)* } }; From 6aacd141ccfc043baaa09e28bd56c55063252976 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Tue, 1 Nov 2022 14:35:25 +0100 Subject: [PATCH 3/3] Use new `method_id` functionality in Foundation --- objc2/src/foundation/array.rs | 17 +++++----- objc2/src/foundation/attributed_string.rs | 34 ++++++++++--------- objc2/src/foundation/bundle.rs | 12 +++---- objc2/src/foundation/data.rs | 5 ++- objc2/src/foundation/dictionary.rs | 10 +++--- objc2/src/foundation/error.rs | 10 +++--- objc2/src/foundation/exception.rs | 17 ++++------ objc2/src/foundation/mutable_array.rs | 11 +++--- .../foundation/mutable_attributed_string.rs | 5 ++- objc2/src/foundation/mutable_data.rs | 5 ++- objc2/src/foundation/mutable_dictionary.rs | 11 +++--- objc2/src/foundation/mutable_set.rs | 11 +++--- objc2/src/foundation/mutable_string.rs | 5 ++- objc2/src/foundation/number.rs | 5 ++- objc2/src/foundation/object.rs | 5 ++- objc2/src/foundation/process_info.rs | 12 +++---- objc2/src/foundation/set.rs | 28 +++++++-------- objc2/src/foundation/string.rs | 23 ++++++------- objc2/src/foundation/thread.rs | 15 ++++---- objc2/src/foundation/uuid.rs | 10 +++--- 20 files changed, 111 insertions(+), 140 deletions(-) diff --git a/objc2/src/foundation/array.rs b/objc2/src/foundation/array.rs index 2a136be92..b00f1071b 100644 --- a/objc2/src/foundation/array.rs +++ b/objc2/src/foundation/array.rs @@ -90,15 +90,14 @@ extern_methods!( /// Generic creation methods. unsafe impl NSArray { /// Get an empty array. - pub fn new() -> Id { - // SAFETY: - // - `new` may not create a new object, but instead return a shared - // instance. We remedy this by returning `Id`. - // - `O` don't actually matter here! E.g. `NSArray` is - // perfectly legal, since the array doesn't have any elements, and - // hence the notion of ownership over the elements is void. - unsafe { msg_send_id![Self::class(), new] } - } + // SAFETY: + // - `new` may not create a new object, but instead return a shared + // instance. We remedy this by returning `Id`. + // - `O` don't actually matter here! E.g. `NSArray` is + // perfectly legal, since the array doesn't have any elements, and + // hence the notion of ownership over the elements is void. + #[method_id(new)] + pub fn new() -> Id; pub fn from_vec(vec: Vec>) -> Id { // SAFETY: diff --git a/objc2/src/foundation/attributed_string.rs b/objc2/src/foundation/attributed_string.rs index 42bfbd79b..15f4928d3 100644 --- a/objc2/src/foundation/attributed_string.rs +++ b/objc2/src/foundation/attributed_string.rs @@ -4,9 +4,9 @@ use core::panic::{RefUnwindSafe, UnwindSafe}; use super::{ NSCopying, NSDictionary, NSMutableAttributedString, NSMutableCopying, NSObject, NSString, }; -use crate::rc::{DefaultId, Id, Shared}; +use crate::rc::{Allocated, DefaultId, Id, Shared}; use crate::runtime::Object; -use crate::{extern_class, extern_methods, msg_send_id, ClassType}; +use crate::{extern_class, extern_methods, ClassType}; extern_class!( /// A string that has associated attributes for portions of its text. @@ -45,9 +45,18 @@ extern_methods!( /// Creating attributed strings. unsafe impl NSAttributedString { /// Construct an empty attributed string. - pub fn new() -> Id { - unsafe { msg_send_id![Self::class(), new] } - } + #[method_id(new)] + pub fn new() -> Id; + + #[method_id(initWithString:attributes:)] + fn init_with_attributes( + this: Option>, + string: &NSString, + attributes: &NSDictionary, + ) -> Id; + + #[method_id(initWithString:)] + fn init_with_string(this: Option>, string: &NSString) -> Id; /// Creates a new attributed string from the given string and attributes. /// @@ -59,28 +68,21 @@ extern_methods!( // TODO: Mutability of the dictionary should be (Shared, Shared) attributes: &NSDictionary, ) -> Id { - unsafe { - msg_send_id![ - Self::alloc(), - initWithString: string, - attributes: attributes, - ] - } + Self::init_with_attributes(Self::alloc(), string, attributes) } /// Creates a new attributed string without any attributes. #[doc(alias = "initWithString:")] pub fn from_nsstring(string: &NSString) -> Id { - unsafe { msg_send_id![Self::alloc(), initWithString: string] } + Self::init_with_string(Self::alloc(), string) } } /// Querying. unsafe impl NSAttributedString { // TODO: Lifetimes? - pub fn string(&self) -> Id { - unsafe { msg_send_id![self, string] } - } + #[method_id(string)] + pub fn string(&self) -> Id; /// Alias for `self.string().len_utf16()`. #[doc(alias = "length")] diff --git a/objc2/src/foundation/bundle.rs b/objc2/src/foundation/bundle.rs index 93d0a5b5f..0d3495099 100644 --- a/objc2/src/foundation/bundle.rs +++ b/objc2/src/foundation/bundle.rs @@ -3,7 +3,7 @@ use core::panic::{RefUnwindSafe, UnwindSafe}; use super::{NSCopying, NSDictionary, NSObject, NSString}; use crate::rc::{Id, Shared}; -use crate::{extern_class, extern_methods, msg_send_id, ClassType}; +use crate::{extern_class, extern_methods, ClassType}; extern_class!( /// A representation of the code and resources stored in a bundle @@ -27,13 +27,11 @@ impl RefUnwindSafe for NSBundle {} extern_methods!( unsafe impl NSBundle { - pub fn main() -> Id { - unsafe { msg_send_id![Self::class(), mainBundle] } - } + #[method_id(mainBundle)] + pub fn main() -> Id; - pub fn info(&self) -> Id, Shared> { - unsafe { msg_send_id![self, infoDictionary] } - } + #[method_id(infoDictionary)] + pub fn info(&self) -> Id, Shared>; pub fn name(&self) -> Option> { // TODO: Use ns_string! diff --git a/objc2/src/foundation/data.rs b/objc2/src/foundation/data.rs index 17e013a3f..6c75bdbed 100644 --- a/objc2/src/foundation/data.rs +++ b/objc2/src/foundation/data.rs @@ -36,9 +36,8 @@ impl RefUnwindSafe for NSData {} extern_methods!( /// Creation methods. unsafe impl NSData { - pub fn new() -> Id { - unsafe { msg_send_id![Self::class(), new] } - } + #[method_id(new)] + pub fn new() -> Id; pub fn with_bytes(bytes: &[u8]) -> Id { unsafe { Id::cast(with_slice(Self::class(), bytes)) } diff --git a/objc2/src/foundation/dictionary.rs b/objc2/src/foundation/dictionary.rs index ce5a74218..efd56f5ad 100644 --- a/objc2/src/foundation/dictionary.rs +++ b/objc2/src/foundation/dictionary.rs @@ -32,9 +32,8 @@ impl UnwindSafe for NSDictiona impl RefUnwindSafe for NSDictionary {} extern_methods!( unsafe impl NSDictionary { - pub fn new() -> Id { - unsafe { msg_send_id![Self::class(), new] } - } + #[method_id(new)] + pub fn new() -> Id; #[doc(alias = "count")] #[method(count)] @@ -102,9 +101,8 @@ extern_methods!( } } - pub fn keys_array(&self) -> Id, Shared> { - unsafe { msg_send_id![self, allKeys] } - } + #[method_id(allKeys)] + pub fn keys_array(&self) -> Id, Shared>; pub fn from_keys_and_objects(keys: &[&T], vals: Vec>) -> Id where diff --git a/objc2/src/foundation/error.rs b/objc2/src/foundation/error.rs index 146f61488..05a6d9dd3 100644 --- a/objc2/src/foundation/error.rs +++ b/objc2/src/foundation/error.rs @@ -63,16 +63,14 @@ extern_methods!( /// Accessor methods. unsafe impl NSError { - pub fn domain(&self) -> Id { - unsafe { msg_send_id![self, domain] } - } + #[method_id(domain)] + pub fn domain(&self) -> Id; #[method(code)] pub fn code(&self) -> NSInteger; - pub fn user_info(&self) -> Option, Shared>> { - unsafe { msg_send_id![self, userInfo] } - } + #[method_id(userInfo)] + pub fn user_info(&self) -> Option, Shared>>; pub fn localized_description(&self) -> Id { // TODO: For some reason this leaks a lot? diff --git a/objc2/src/foundation/exception.rs b/objc2/src/foundation/exception.rs index 284d7f7ea..d6a578126 100644 --- a/objc2/src/foundation/exception.rs +++ b/objc2/src/foundation/exception.rs @@ -83,20 +83,17 @@ extern_methods!( /// can take. /// /// [doc]: https://developer.apple.com/documentation/foundation/nsexceptionname?language=objc - pub fn name(&self) -> Id { - // Nullability not documented, but a name is expected in most places. - unsafe { msg_send_id![self, name] } - } + #[method_id(name)] + // Nullability not documented, but a name is expected in most places. + pub fn name(&self) -> Id; /// A human-readable message summarizing the reason for the exception. - pub fn reason(&self) -> Option> { - unsafe { msg_send_id![self, reason] } - } + #[method_id(reason)] + pub fn reason(&self) -> Option>; /// Application-specific data pertaining to the exception. - pub fn user_info(&self) -> Option, Shared>> { - unsafe { msg_send_id![self, userInfo] } - } + #[method_id(userInfo)] + pub fn user_info(&self) -> Option, Shared>>; /// Convert this into an [`Exception`] object. pub fn into_exception(this: Id) -> Id { diff --git a/objc2/src/foundation/mutable_array.rs b/objc2/src/foundation/mutable_array.rs index 40cebb2da..c00992b13 100644 --- a/objc2/src/foundation/mutable_array.rs +++ b/objc2/src/foundation/mutable_array.rs @@ -11,7 +11,7 @@ use super::{ NSObject, }; use crate::rc::{DefaultId, Id, Owned, Ownership, Shared, SliceId}; -use crate::{ClassType, Message, __inner_extern_class, extern_methods, msg_send, msg_send_id}; +use crate::{ClassType, Message, __inner_extern_class, extern_methods, msg_send}; __inner_extern_class!( /// A growable ordered collection of objects. @@ -42,11 +42,10 @@ unsafe impl Send for NSMutableArray {} extern_methods!( /// Generic creation methods. unsafe impl NSMutableArray { - pub fn new() -> Id { - // SAFETY: Same as `NSArray::new`, except mutable arrays are always - // unique. - unsafe { msg_send_id![Self::class(), new] } - } + // SAFETY: Same as `NSArray::new`, except mutable arrays are always + // unique. + #[method_id(new)] + pub fn new() -> Id; pub fn from_vec(vec: Vec>) -> Id { // SAFETY: Same as `NSArray::from_vec`, except mutable arrays are diff --git a/objc2/src/foundation/mutable_attributed_string.rs b/objc2/src/foundation/mutable_attributed_string.rs index 8ee92969a..d08d7ea78 100644 --- a/objc2/src/foundation/mutable_attributed_string.rs +++ b/objc2/src/foundation/mutable_attributed_string.rs @@ -21,9 +21,8 @@ extern_methods!( /// Creating mutable attributed strings. unsafe impl NSMutableAttributedString { /// Construct an empty mutable attributed string. - pub fn new() -> Id { - unsafe { msg_send_id![Self::class(), new] } - } + #[method_id(new)] + pub fn new() -> Id; // TODO: new_with_attributes diff --git a/objc2/src/foundation/mutable_data.rs b/objc2/src/foundation/mutable_data.rs index 6b37ee0cd..6509d7600 100644 --- a/objc2/src/foundation/mutable_data.rs +++ b/objc2/src/foundation/mutable_data.rs @@ -31,9 +31,8 @@ extern_class!( extern_methods!( /// Creation methods unsafe impl NSMutableData { - pub fn new() -> Id { - unsafe { msg_send_id![Self::class(), new] } - } + #[method_id(new)] + pub fn new() -> Id; pub fn with_bytes(bytes: &[u8]) -> Id { unsafe { Id::from_shared(Id::cast(with_slice(Self::class(), bytes))) } diff --git a/objc2/src/foundation/mutable_dictionary.rs b/objc2/src/foundation/mutable_dictionary.rs index 1a91704a9..8ec36a9b5 100644 --- a/objc2/src/foundation/mutable_dictionary.rs +++ b/objc2/src/foundation/mutable_dictionary.rs @@ -52,12 +52,11 @@ extern_methods!( /// /// let dict = NSMutableDictionary::::new(); /// ``` - pub fn new() -> Id { - // SAFETY: - // Mutable dictionaries are always unique, so it's safe to return - // `Id` - unsafe { msg_send_id![Self::class(), new] } - } + // SAFETY: + // Mutable dictionaries are always unique, so it's safe to return + // `Id` + #[method_id(new)] + pub fn new() -> Id; #[method(setDictionary:)] fn set_dictionary(&mut self, dict: &NSDictionary); diff --git a/objc2/src/foundation/mutable_set.rs b/objc2/src/foundation/mutable_set.rs index 8dca59053..03f44b5a8 100644 --- a/objc2/src/foundation/mutable_set.rs +++ b/objc2/src/foundation/mutable_set.rs @@ -5,7 +5,7 @@ use core::marker::PhantomData; use super::set::with_objects; use super::{NSCopying, NSFastEnumeration, NSFastEnumerator, NSMutableCopying, NSObject, NSSet}; use crate::rc::{DefaultId, Id, Owned, Ownership, Shared, SliceId}; -use crate::{ClassType, Message, __inner_extern_class, extern_methods, msg_send_id}; +use crate::{ClassType, Message, __inner_extern_class, extern_methods}; __inner_extern_class!( /// A growable unordered collection of unique objects. @@ -44,11 +44,10 @@ extern_methods!( /// /// let set = NSMutableSet::::new(); /// ``` - pub fn new() -> Id { - // SAFETY: - // Same as `NSSet::new`, except mutable sets are always unique. - unsafe { msg_send_id![Self::class(), new] } - } + // SAFETY: + // Same as `NSSet::new`, except mutable sets are always unique. + #[method_id(new)] + pub fn new() -> Id; /// Creates an [`NSMutableSet`] from a vector. /// diff --git a/objc2/src/foundation/mutable_string.rs b/objc2/src/foundation/mutable_string.rs index 748f1258a..c0253ff03 100644 --- a/objc2/src/foundation/mutable_string.rs +++ b/objc2/src/foundation/mutable_string.rs @@ -24,9 +24,8 @@ extern_methods!( /// Creating mutable strings. unsafe impl NSMutableString { /// Construct an empty [`NSMutableString`]. - pub fn new() -> Id { - unsafe { msg_send_id![Self::class(), new] } - } + #[method_id(new)] + pub fn new() -> Id; /// Creates a new [`NSMutableString`] by copying the given string slice. #[doc(alias = "initWithBytes:length:encoding:")] diff --git a/objc2/src/foundation/number.rs b/objc2/src/foundation/number.rs index ec3e76dfb..103550ef0 100644 --- a/objc2/src/foundation/number.rs +++ b/objc2/src/foundation/number.rs @@ -244,9 +244,8 @@ extern_methods!( #[method(isEqualToNumber:)] fn is_equal_to_number(&self, other: &Self) -> bool; - fn string(&self) -> Id { - unsafe { msg_send_id![self, stringValue] } - } + #[method_id(stringValue)] + fn string(&self) -> Id; } ); diff --git a/objc2/src/foundation/object.rs b/objc2/src/foundation/object.rs index 7af14be16..b60467ef5 100644 --- a/objc2/src/foundation/object.rs +++ b/objc2/src/foundation/object.rs @@ -35,9 +35,8 @@ unsafe impl ClassType for NSObject { extern_methods!( unsafe impl NSObject { - pub fn new() -> Id { - unsafe { msg_send_id![Self::class(), new] } - } + #[method_id(new)] + pub fn new() -> Id; #[method(isKindOfClass:)] fn is_kind_of_inner(&self, cls: &Class) -> bool; diff --git a/objc2/src/foundation/process_info.rs b/objc2/src/foundation/process_info.rs index a0cb1d23f..a70567f60 100644 --- a/objc2/src/foundation/process_info.rs +++ b/objc2/src/foundation/process_info.rs @@ -3,7 +3,7 @@ use core::panic::{RefUnwindSafe, UnwindSafe}; use super::{NSObject, NSString}; use crate::rc::{Id, Shared}; -use crate::{extern_class, extern_methods, msg_send_id, ClassType}; +use crate::{extern_class, extern_methods, ClassType}; extern_class!( /// A collection of information about the current process. @@ -27,13 +27,11 @@ impl RefUnwindSafe for NSProcessInfo {} extern_methods!( unsafe impl NSProcessInfo { - pub fn process_info() -> Id { - unsafe { msg_send_id![Self::class(), processInfo] } - } + #[method_id(processInfo)] + pub fn process_info() -> Id; - pub fn process_name(&self) -> Id { - unsafe { msg_send_id![self, processName] } - } + #[method_id(processName)] + pub fn process_name(&self) -> Id; // TODO: This contains a lot more important functionality! } diff --git a/objc2/src/foundation/set.rs b/objc2/src/foundation/set.rs index 7c04e2e06..18a344ebc 100644 --- a/objc2/src/foundation/set.rs +++ b/objc2/src/foundation/set.rs @@ -64,15 +64,14 @@ extern_methods!( /// /// let set = NSSet::::new(); /// ``` - pub fn new() -> Id { - // SAFETY: - // - `new` may not create a new object, but instead return a shared - // instance. We remedy this by returning `Id`. - // - `O` don't actually matter here! E.g. `NSSet` is - // perfectly legal, since the set doesn't have any elements, and - // hence the notion of ownership over the elements is void. - unsafe { msg_send_id![Self::class(), new] } - } + // SAFETY: + // - `new` may not create a new object, but instead return a shared + // instance. We remedy this by returning `Id`. + // - `O` don't actually matter here! E.g. `NSSet` is + // perfectly legal, since the set doesn't have any elements, and + // hence the notion of ownership over the elements is void. + #[method_id(new)] + pub fn new() -> Id; /// Creates an [`NSSet`] from a vector. /// @@ -234,13 +233,12 @@ extern_methods!( /// assert_eq!(set.to_array().len(), 3); /// assert!(set.to_array().iter().all(|i| nums.contains(&i.as_i32()))); /// ``` + // SAFETY: + // We only define this method for sets with shared elements + // because we can't return copies of owned elements. + #[method_id(allObjects)] #[doc(alias = "allObjects")] - pub fn to_array(&self) -> Id, Shared> { - // SAFETY: - // We only define this method for sets with shared elements - // because we can't return copies of owned elements. - unsafe { msg_send_id![self, allObjects] } - } + pub fn to_array(&self) -> Id, Shared>; } // We're explicit about `T` being `PartialEq` for these methods because the diff --git a/objc2/src/foundation/string.rs b/objc2/src/foundation/string.rs index a2bb38225..a9281f384 100644 --- a/objc2/src/foundation/string.rs +++ b/objc2/src/foundation/string.rs @@ -12,7 +12,7 @@ use std::os::raw::c_char; use super::{NSComparisonResult, NSCopying, NSError, NSMutableCopying, NSMutableString, NSObject}; use crate::rc::{autoreleasepool, AutoreleasePool, DefaultId, Id, Shared}; use crate::runtime::{Class, Object}; -use crate::{extern_class, extern_methods, msg_send, msg_send_id, ClassType}; +use crate::{extern_class, extern_methods, msg_send, ClassType}; #[cfg(feature = "apple")] const UTF8_ENCODING: usize = 4; @@ -52,9 +52,8 @@ impl RefUnwindSafe for NSString {} extern_methods!( unsafe impl NSString { /// Construct an empty NSString. - pub fn new() -> Id { - unsafe { msg_send_id![Self::class(), new] } - } + #[method_id(new)] + pub fn new() -> Id; /// Create a new string by appending the given string to self. /// @@ -70,13 +69,12 @@ extern_methods!( /// let error_message = error_tag.concat(error_string); /// assert_eq!(&*error_message, ns_string!("Error: premature end of file.")); /// ``` + // SAFETY: The other string is non-null, and won't be retained by the + // function. + #[method_id(stringByAppendingString:)] #[doc(alias = "stringByAppendingString")] #[doc(alias = "stringByAppendingString:")] - pub fn concat(&self, other: &Self) -> Id { - // SAFETY: The other string is non-null, and won't be retained - // by the function. - unsafe { msg_send_id![self, stringByAppendingString: other] } - } + pub fn concat(&self, other: &Self) -> Id; /// Create a new string by appending the given string, separated by /// a path separator. @@ -100,12 +98,11 @@ extern_methods!( /// assert_eq!(&*ns_string!("/").join_path(extension), ns_string!("/scratch.tiff")); /// assert_eq!(&*ns_string!("").join_path(extension), ns_string!("scratch.tiff")); /// ``` + // SAFETY: Same as `Self::concat`. + #[method_id(stringByAppendingPathComponent:)] #[doc(alias = "stringByAppendingPathComponent")] #[doc(alias = "stringByAppendingPathComponent:")] - pub fn join_path(&self, other: &Self) -> Id { - // SAFETY: Same as `Self::concat`. - unsafe { msg_send_id![self, stringByAppendingPathComponent: other] } - } + pub fn join_path(&self, other: &Self) -> Id; /// The number of UTF-8 code units in `self`. #[doc(alias = "lengthOfBytesUsingEncoding")] diff --git a/objc2/src/foundation/thread.rs b/objc2/src/foundation/thread.rs index 24f4c1762..52529554f 100644 --- a/objc2/src/foundation/thread.rs +++ b/objc2/src/foundation/thread.rs @@ -27,9 +27,8 @@ impl RefUnwindSafe for NSThread {} extern_methods!( unsafe impl NSThread { /// Returns the [`NSThread`] object representing the current thread. - pub fn current() -> Id { - unsafe { msg_send_id![Self::class(), currentThread] } - } + #[method_id(currentThread)] + pub fn current() -> Id; /// Returns the [`NSThread`] object representing the main thread. pub fn main() -> Id { @@ -44,13 +43,11 @@ extern_methods!( pub fn is_main(&self) -> bool; /// The name of the thread. - pub fn name(&self) -> Option> { - unsafe { msg_send_id![self, name] } - } + #[method_id(name)] + pub fn name(&self) -> Option>; - unsafe fn new() -> Id { - unsafe { msg_send_id![Self::class(), new] } - } + #[method_id(new)] + unsafe fn new() -> Id; #[method(start)] unsafe fn start(&self); diff --git a/objc2/src/foundation/uuid.rs b/objc2/src/foundation/uuid.rs index 6282dec1e..c47e6245d 100644 --- a/objc2/src/foundation/uuid.rs +++ b/objc2/src/foundation/uuid.rs @@ -45,9 +45,8 @@ impl RefUnwindSafe for NSUUID {} extern_methods!( unsafe impl NSUUID { - pub fn new_v4() -> Id { - unsafe { msg_send_id![Self::class(), new] } - } + #[method_id(new)] + pub fn new_v4() -> Id; /// The 'nil UUID'. pub fn nil() -> Id { @@ -72,9 +71,8 @@ extern_methods!( bytes.0 } - pub fn string(&self) -> Id { - unsafe { msg_send_id![self, UUIDString] } - } + #[method_id(UUIDString)] + pub fn string(&self) -> Id; } );