From 41532597046040bd955dc643446cba9e81acf4cf Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Mon, 16 Jan 2023 10:00:08 +0100 Subject: [PATCH 1/6] Refactor declare_class! internals a bit --- crates/objc2/src/declare.rs | 46 +++++++++++++ crates/objc2/src/macros/declare_class.rs | 85 ++++++++++-------------- 2 files changed, 82 insertions(+), 49 deletions(-) diff --git a/crates/objc2/src/declare.rs b/crates/objc2/src/declare.rs index 7b22fca74..3941e8f43 100644 --- a/crates/objc2/src/declare.rs +++ b/crates/objc2/src/declare.rs @@ -924,4 +924,50 @@ mod tests { let _cls = Custom::class(); } + + // Proof-of-concept how we could make declare_class! accept generic. + #[test] + fn test_generic() { + struct GenericDeclareClass(T); + + unsafe impl RefEncode for GenericDeclareClass { + const ENCODING_REF: Encoding = Encoding::Object; + } + unsafe impl Message for GenericDeclareClass {} + + unsafe impl ClassType for GenericDeclareClass { + type Super = NSObject; + const NAME: &'static str = "GenericDeclareClass"; + + #[inline] + fn as_super(&self) -> &Self::Super { + unimplemented!() + } + + #[inline] + fn as_super_mut(&mut self) -> &mut Self::Super { + unimplemented!() + } + + fn class() -> &'static Class { + let superclass = NSObject::class(); + let mut builder = ClassBuilder::new(Self::NAME, superclass).unwrap(); + + unsafe { + builder.add_method( + sel!(generic), + >::generic as unsafe extern "C" fn(_, _), + ); + } + + builder.register() + } + } + + impl GenericDeclareClass { + extern "C" fn generic(&self, _cmd: Sel) {} + } + + let _ = GenericDeclareClass::<()>::class(); + } } diff --git a/crates/objc2/src/macros/declare_class.rs b/crates/objc2/src/macros/declare_class.rs index a8a61a8d1..e01c75dd0 100644 --- a/crates/objc2/src/macros/declare_class.rs +++ b/crates/objc2/src/macros/declare_class.rs @@ -412,10 +412,10 @@ macro_rules! declare_class { } // Implement protocols and methods - $crate::__declare_class_methods!( - @register_out(builder) + $crate::__declare_class_register_methods! { + @(builder) $($methods)* - ); + } let _cls = builder.register(); }); @@ -459,36 +459,23 @@ macro_rules! declare_class { } // Methods - $crate::__declare_class_methods!( - @method_out + $crate::__declare_class_methods! { $($methods)* - ); + } }; + + // Allow declaring class with no instance variables { $(#[$m:meta])* $v:vis struct $name:ident; - unsafe impl ClassType for $for:ty { - $(#[inherits($($inheritance_rest:ty),+)])? - type Super = $superclass:ty; - - $(const NAME: &'static str = $name_const:literal;)? - } - - $($methods:tt)* + $($rest:tt)* } => { $crate::declare_class! { $(#[$m])* $v struct $name {} - unsafe impl ClassType for $for { - $(#[inherits($($inheritance_rest),+)])? - type Super = $superclass; - - $(const NAME: &'static str = $name_const;)? - } - - $($methods)* + $($rest)* } }; } @@ -507,11 +494,10 @@ macro_rules! __select_name { #[doc(hidden)] #[macro_export] macro_rules! __declare_class_methods { - (@method_out) => {}; + // Base-case + () => {}; // With protocol ( - @method_out - $(#[$m:meta])* unsafe impl ConformsTo<$protocol:ty> for $for:ty { $($methods:tt)* @@ -532,15 +518,12 @@ macro_rules! __declare_class_methods { } } - $crate::__declare_class_methods!( - @method_out + $crate::__declare_class_methods!{ $($rest)* - ); + } }; // Without protocol ( - @method_out - $(#[$m:meta])* unsafe impl $for:ty { $($methods:tt)* @@ -557,16 +540,20 @@ macro_rules! __declare_class_methods { } } - $crate::__declare_class_methods!( - @method_out + $crate::__declare_class_methods! { $($rest)* - ); + } }; +} - (@register_out($builder:ident)) => {}; +#[doc(hidden)] +#[macro_export] +macro_rules! __declare_class_register_methods { + // Base-case + (@($builder:ident)) => {}; // With protocol ( - @register_out($builder:ident) + @($builder:ident) $(#[$($m:tt)*])* unsafe impl ConformsTo<$protocol:ty> for $for:ty { @@ -603,14 +590,14 @@ macro_rules! __declare_class_methods { ) } - $crate::__declare_class_methods!( - @register_out($builder) + $crate::__declare_class_register_methods! { + @($builder) $($rest)* - ); + } }; // Without protocol ( - @register_out($builder:ident) + @($builder:ident) $(#[$($m:tt)*])* unsafe impl $for:ty { @@ -638,10 +625,10 @@ macro_rules! __declare_class_methods { ) } - $crate::__declare_class_methods!( - @register_out($builder) + $crate::__declare_class_register_methods! { + @($builder) $($rest)* - ); + } }; } @@ -649,13 +636,13 @@ macro_rules! __declare_class_methods { #[macro_export] macro_rules! __declare_class_rewrite_methods { { - @($($macro:tt)*) + @($out_macro:path) @($($macro_arguments:tt)*) } => {}; // Unsafe variant { - @($($macro:tt)*) + @($out_macro:path) @($($macro_arguments:tt)*) $(#[$($m:tt)*])* @@ -664,7 +651,7 @@ macro_rules! __declare_class_rewrite_methods { $($rest:tt)* } => { $crate::__rewrite_self_arg! { - ($($macro)*) + ($out_macro) ($($args)*) // Split the function into parts, and send the arguments down to @@ -681,7 +668,7 @@ macro_rules! __declare_class_rewrite_methods { } $crate::__declare_class_rewrite_methods! { - @($($macro)*) + @($out_macro) @($($macro_arguments)*) $($rest)* @@ -690,7 +677,7 @@ macro_rules! __declare_class_rewrite_methods { // Safe variant { - @($($macro:tt)*) + @($out_macro:path) @($($macro_arguments:tt)*) $(#[$($m:tt)*])* @@ -699,7 +686,7 @@ macro_rules! __declare_class_rewrite_methods { $($rest:tt)* } => { $crate::__rewrite_self_arg! { - ($($macro)*) + ($out_macro) ($($args)*) @($($macro_arguments)*) @@ -713,7 +700,7 @@ macro_rules! __declare_class_rewrite_methods { } $crate::__declare_class_rewrite_methods! { - @($($macro)*) + @($out_macro) @($($macro_arguments)*) $($rest)* From 54637aef7c364837c2f41c986f67b6a75a69eeb0 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Mon, 16 Jan 2023 02:30:28 +0100 Subject: [PATCH 2/6] Fix mutable and `_: bool` parameters in declare_class! --- .../objc2/src/declare/declare_class_tests.rs | 51 ++++++++++++++++ crates/objc2/src/macros/declare_class.rs | 59 +++++++++++++++---- 2 files changed, 99 insertions(+), 11 deletions(-) diff --git a/crates/objc2/src/declare/declare_class_tests.rs b/crates/objc2/src/declare/declare_class_tests.rs index 38e28adb3..cbd272377 100644 --- a/crates/objc2/src/declare/declare_class_tests.rs +++ b/crates/objc2/src/declare/declare_class_tests.rs @@ -246,3 +246,54 @@ fn test_multiple_colon_selector() { assert!(obj.test_error(1, 2).is_ok()); assert!(obj.test_object(1, 2, 3, ptr::null()).is_none()); } + +declare_class!( + struct DeclareClassAllTheBool; + + unsafe impl ClassType for DeclareClassAllTheBool { + type Super = NSObject; + } + + unsafe impl DeclareClassAllTheBool { + #[method(returnsBool)] + fn returns_bool() -> bool { + true + } + + #[method(returnsBoolInstance)] + fn returns_bool_instance(&self) -> bool { + true + } + + #[method(takesBool:andMut:andUnderscore:)] + fn takes_bool(a: bool, mut b: bool, _: bool) -> bool { + if b { + b = a; + } + b + } + + #[method(takesBoolInstance:andMut:andUnderscore:)] + fn takes_bool_instance(&self, a: bool, mut b: bool, _: bool) -> bool { + if b { + b = a; + } + b + } + + #[method(takesReturnsBool:)] + fn takes_returns_bool(b: bool) -> bool { + b + } + + #[method(takesReturnsBoolInstance:)] + fn takes_returns_bool_instance(&self, b: bool) -> bool { + b + } + } +); + +#[test] +fn test_all_the_bool() { + let _ = DeclareClassAllTheBool::class(); +} diff --git a/crates/objc2/src/macros/declare_class.rs b/crates/objc2/src/macros/declare_class.rs index e01c75dd0..276038b0e 100644 --- a/crates/objc2/src/macros/declare_class.rs +++ b/crates/objc2/src/macros/declare_class.rs @@ -793,7 +793,7 @@ macro_rules! __fn_args { // Ignore `_` { ($out_macro:path) - (_: $param_ty:ty, $($rest:tt)*) + (_ : $param_ty:ty, $($rest:tt)*) ($($args_converted:tt)*) ($($body_prefix:tt)*) $($macro_args:tt)* @@ -801,7 +801,7 @@ macro_rules! __fn_args { $crate::__fn_args! { ($out_macro) ($($rest)*) - ($($args_converted)* _: $param_ty,) + ($($args_converted)* _ : <$param_ty as $crate::encode::EncodeConvert>::__Inner,) ($($body_prefix)*) $($macro_args)* } @@ -809,7 +809,7 @@ macro_rules! __fn_args { // Convert mut { ($out_macro:path) - (mut $param:ident: $param_ty:ty, $($rest:tt)*) + (mut $param:ident : $param_ty:ty, $($rest:tt)*) ($($args_converted:tt)*) ($($body_prefix:tt)*) $($macro_args:tt)* @@ -817,7 +817,7 @@ macro_rules! __fn_args { $crate::__fn_args! { ($out_macro) ($($rest)*) - ($($args_converted)* $param: <$param_ty as $crate::encode::EncodeConvert>::__Inner,) + ($($args_converted)* $param : <$param_ty as $crate::encode::EncodeConvert>::__Inner,) ( $($body_prefix)* let mut $param = <$param_ty as $crate::encode::EncodeConvert>::__from_inner($param); @@ -828,7 +828,7 @@ macro_rules! __fn_args { // Convert { ($out_macro:path) - ($param:ident: $param_ty:ty, $($rest:tt)*) + ($param:ident : $param_ty:ty, $($rest:tt)*) ($($args_converted:tt)*) ($($body_prefix:tt)*) $($macro_args:tt)* @@ -836,7 +836,7 @@ macro_rules! __fn_args { $crate::__fn_args! { ($out_macro) ($($rest)*) - ($($args_converted)* $param: <$param_ty as $crate::encode::EncodeConvert>::__Inner,) + ($($args_converted)* $param : <$param_ty as $crate::encode::EncodeConvert>::__Inner,) ( $($body_prefix)* let $param = <$param_ty as $crate::encode::EncodeConvert>::__from_inner($param); @@ -892,7 +892,9 @@ macro_rules! __declare_class_register_out { @() }, Self::$name as $crate::__fn_ptr! { - @($($qualifiers)*) $($args_start)* $($args_rest)* + @($($qualifiers)*) + @(_, _,) + $($args_rest)* }, ); ) @@ -928,7 +930,9 @@ macro_rules! __declare_class_register_out { @() }, Self::$name as $crate::__fn_ptr! { - @($($qualifiers)*) $($args_start)* $($args_rest)* + @($($qualifiers)*) + @(_, _,) + $($args_rest)* }, ); ) @@ -966,9 +970,42 @@ macro_rules! __declare_class_register_out { macro_rules! __fn_ptr { ( @($($qualifiers:tt)*) - $($(mut)? $($param:ident)? $(_)?: $param_ty:ty),* $(,)? + @($($output:tt)*) + $(,)? ) => { - $($qualifiers)* fn($($crate::__fn_ptr!(@__to_anonymous $param_ty)),*) -> _ + $($qualifiers)* fn($($output)*) -> _ + }; + ( + @($($qualifiers:tt)*) + @($($output:tt)*) + _ : $param_ty:ty $(, $($rest:tt)*)? + ) => { + $crate::__fn_ptr! { + @($($qualifiers)*) + @($($output)* _,) + $($($rest)*)? + } + }; + ( + @($($qualifiers:tt)*) + @($($output:tt)*) + mut $param:ident : $param_ty:ty $(, $($rest:tt)*)? + ) => { + $crate::__fn_ptr! { + @($($qualifiers)*) + @($($output)* _,) + $($($rest)*)? + } + }; + ( + @($($qualifiers:tt)*) + @($($output:tt)*) + $param:ident : $param_ty:ty $(, $($rest:tt)*)? + ) => { + $crate::__fn_ptr! { + @($($qualifiers)*) + @($($output)* _,) + $($($rest)*)? + } }; - (@__to_anonymous $param_ty:ty) => { _ } } From 71480254e8666f0a70efb6c0151d4cf69432db6e Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Mon, 16 Jan 2023 05:51:13 +0100 Subject: [PATCH 3/6] Add more declare_class! UI tests --- .../ui/declare_class_invalid_receiver.rs | 30 ++++ .../ui/declare_class_invalid_receiver.stderr | 95 ++++++++++++ .../ui/declare_class_invalid_syntax.rs | 48 ++++++ .../ui/declare_class_invalid_syntax.stderr | 138 ++++++++++++++++++ .../test-ui/ui/declare_class_invalid_type.rs | 35 +++++ .../ui/declare_class_invalid_type.stderr | 119 +++++++++++++++ 6 files changed, 465 insertions(+) create mode 100644 crates/test-ui/ui/declare_class_invalid_receiver.rs create mode 100644 crates/test-ui/ui/declare_class_invalid_receiver.stderr create mode 100644 crates/test-ui/ui/declare_class_invalid_syntax.rs create mode 100644 crates/test-ui/ui/declare_class_invalid_syntax.stderr create mode 100644 crates/test-ui/ui/declare_class_invalid_type.rs create mode 100644 crates/test-ui/ui/declare_class_invalid_type.stderr diff --git a/crates/test-ui/ui/declare_class_invalid_receiver.rs b/crates/test-ui/ui/declare_class_invalid_receiver.rs new file mode 100644 index 000000000..c6e440978 --- /dev/null +++ b/crates/test-ui/ui/declare_class_invalid_receiver.rs @@ -0,0 +1,30 @@ +use objc2::rc::{Id, Shared}; +use objc2::{declare_class, ClassType}; +use objc2::runtime::NSObject; + +declare_class!( + struct CustomObject; + + unsafe impl ClassType for CustomObject { + type Super = NSObject; + } + + unsafe impl CustomObject { + #[method(test1)] + fn test1(self: Box) { + unimplemented!() + } + + #[method(test2)] + fn test2(this: Id) { + unimplemented!() + } + + #[method(test3)] + fn test3(this: Self) { + unimplemented!() + } + } +); + +fn main() {} diff --git a/crates/test-ui/ui/declare_class_invalid_receiver.stderr b/crates/test-ui/ui/declare_class_invalid_receiver.stderr new file mode 100644 index 000000000..b0b49723b --- /dev/null +++ b/crates/test-ui/ui/declare_class_invalid_receiver.stderr @@ -0,0 +1,95 @@ +error[E0277]: the trait bound `extern "C" fn(Box, objc2::runtime::Sel): MethodImplementation` is not satisfied + --> ui/declare_class_invalid_receiver.rs + | + | / declare_class!( + | | struct CustomObject; + | | + | | unsafe impl ClassType for CustomObject { +... | + | | } + | | ); + | | ^ + | | | + | |_the trait `MethodImplementation` is not implemented for `extern "C" fn(Box, objc2::runtime::Sel)` + | required by a bound introduced by this call + | + = help: the following other types implement trait `MethodImplementation`: + extern "C" fn(&'a T, objc2::runtime::Sel) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D, E) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D, E, F) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D, E, F, G) -> R + and $N others +note: required by a bound in `ClassBuilder::add_method` + --> $WORKSPACE/crates/objc2/src/declare.rs + | + | F: MethodImplementation, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `ClassBuilder::add_method` + = note: this error originates in the macro `$crate::__declare_class_register_out` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `extern "C" fn(Id, objc2::runtime::Sel): MethodImplementation` is not satisfied + --> ui/declare_class_invalid_receiver.rs + | + | / declare_class!( + | | struct CustomObject; + | | + | | unsafe impl ClassType for CustomObject { +... | + | | } + | | ); + | | ^ + | | | + | |_the trait `MethodImplementation` is not implemented for `extern "C" fn(Id, objc2::runtime::Sel)` + | required by a bound introduced by this call + | + = help: the following other types implement trait `MethodImplementation`: + extern "C" fn(&'a T, objc2::runtime::Sel) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D, E) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D, E, F) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D, E, F, G) -> R + and $N others +note: required by a bound in `ClassBuilder::add_method` + --> $WORKSPACE/crates/objc2/src/declare.rs + | + | F: MethodImplementation, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `ClassBuilder::add_method` + = note: this error originates in the macro `$crate::__declare_class_register_out` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `extern "C" fn(CustomObject, objc2::runtime::Sel): MethodImplementation` is not satisfied + --> ui/declare_class_invalid_receiver.rs + | + | / declare_class!( + | | struct CustomObject; + | | + | | unsafe impl ClassType for CustomObject { +... | + | | } + | | ); + | | ^ + | | | + | |_the trait `MethodImplementation` is not implemented for `extern "C" fn(CustomObject, objc2::runtime::Sel)` + | required by a bound introduced by this call + | + = help: the following other types implement trait `MethodImplementation`: + extern "C" fn(&'a T, objc2::runtime::Sel) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D, E) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D, E, F) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D, E, F, G) -> R + and $N others +note: required by a bound in `ClassBuilder::add_method` + --> $WORKSPACE/crates/objc2/src/declare.rs + | + | F: MethodImplementation, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `ClassBuilder::add_method` + = note: this error originates in the macro `$crate::__declare_class_register_out` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/crates/test-ui/ui/declare_class_invalid_syntax.rs b/crates/test-ui/ui/declare_class_invalid_syntax.rs new file mode 100644 index 000000000..4880b56f5 --- /dev/null +++ b/crates/test-ui/ui/declare_class_invalid_syntax.rs @@ -0,0 +1,48 @@ +use objc2::{declare_class, ClassType}; +use objc2::runtime::NSObject; + +declare_class!( + struct CustomObject; + + unsafe impl ClassType for CustomObject { + type Super = NSObject; + } + + unsafe impl CustomObject { + fn test_no_attribute() { + unimplemented!() + } + + #[method_id(testMethodId)] + fn test_method_id() { + unimplemented!() + } + + #[method(testInvalid)] + fn test_invalid() { + a - + } + + #[method(testPattern:)] + fn test_pattern((a, b): (u32, i32)) { + unimplemented!() + } + + #[method(testSelf)] + fn test_self(self) { + unimplemented!() + } + } + + unsafe impl CustomObject { + #[method(testPub)] + pub fn test_pub() {} + } + + unsafe impl CustomObject { + #[method(testNoBody)] + fn test_no_body(&self); + } +); + +fn main() {} diff --git a/crates/test-ui/ui/declare_class_invalid_syntax.stderr b/crates/test-ui/ui/declare_class_invalid_syntax.stderr new file mode 100644 index 000000000..2b144279a --- /dev/null +++ b/crates/test-ui/ui/declare_class_invalid_syntax.stderr @@ -0,0 +1,138 @@ +error: must specify the desired selector using `#[method(...)]` or `#[method_id(...)]` + --> ui/declare_class_invalid_syntax.rs + | + | / declare_class!( + | | struct CustomObject; + | | + | | unsafe impl ClassType for CustomObject { +... | + | | } + | | ); + | |_^ + | + = note: this error originates in the macro `$crate::__extract_custom_attributes` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `#[method_id(...)]` is not supported in `declare_class!` yet + --> ui/declare_class_invalid_syntax.rs + | + | / declare_class!( + | | struct CustomObject; + | | + | | unsafe impl ClassType for CustomObject { +... | + | | } + | | ); + | |_^ + | + = note: this error originates in the macro `$crate::__declare_class_register_out` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: expected expression, found `}` + --> ui/declare_class_invalid_syntax.rs + | + | } + | ^ expected expression + +error: no rules expected the token `(` + --> ui/declare_class_invalid_syntax.rs + | + | fn test_pattern((a, b): (u32, i32)) { + | ^ no rules expected this token in macro call + | + = note: while trying to match sequence start + +error: unexpected end of macro invocation + --> ui/declare_class_invalid_syntax.rs + | + | fn test_self(self) { + | ^ missing tokens in macro arguments + | +note: while trying to match `:` + --> $WORKSPACE/crates/objc2/src/macros/declare_class.rs + | + | $param:ident : $param_ty:ty $(, $($rest:tt)*)? + | ^ + +error: no rules expected the token `pub` + --> ui/declare_class_invalid_syntax.rs + | + | pub fn test_pub() {} + | ^^^ no rules expected this token in macro call + | +note: while trying to match `unsafe` + --> $WORKSPACE/crates/objc2/src/macros/declare_class.rs + | + | unsafe fn $name:ident($($args:tt)*) $(-> $ret:ty)? $body:block + | ^^^^^^ + +error: no rules expected the token `;` + --> ui/declare_class_invalid_syntax.rs + | + | fn test_no_body(&self); + | ^ no rules expected this token in macro call + | +note: while trying to match meta-variable `$body:block` + --> $WORKSPACE/crates/objc2/src/macros/declare_class.rs + | + | fn $name:ident($($args:tt)*) $(-> $ret:ty)? $body:block + | ^^^^^^^^^^^ + +error: no rules expected the token `(` + --> ui/declare_class_invalid_syntax.rs + | + | fn test_pattern((a, b): (u32, i32)) { + | ^ no rules expected this token in macro call + | +note: while trying to match `_` + --> $WORKSPACE/crates/objc2/src/macros/declare_class.rs + | + | (_ : $param_ty:ty, $($rest:tt)*) + | ^ + +error: no rules expected the token `,` + --> ui/declare_class_invalid_syntax.rs + | + | / declare_class!( + | | struct CustomObject; + | | + | | unsafe impl ClassType for CustomObject { +... | + | | } + | | ); + | |_^ no rules expected this token in macro call + | +note: while trying to match `:` + --> $WORKSPACE/crates/objc2/src/macros/declare_class.rs + | + | ($param:ident : $param_ty:ty, $($rest:tt)*) + | ^ + = note: this error originates in the macro `$crate::__declare_class_method_out` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0599]: no function or associated item named `test_pattern` found for struct `CustomObject` in the current scope + --> ui/declare_class_invalid_syntax.rs + | + | / declare_class!( + | | struct CustomObject; + | | + | | unsafe impl ClassType for CustomObject { +... | + | | fn test_pattern((a, b): (u32, i32)) { + | | ^^^^^^^^^^^^ function or associated item not found in `CustomObject` +... | + | | } + | | ); + | |_- function or associated item `test_pattern` not found for this struct + +error[E0599]: no function or associated item named `test_self` found for struct `CustomObject` in the current scope + --> ui/declare_class_invalid_syntax.rs + | + | / declare_class!( + | | struct CustomObject; + | | + | | unsafe impl ClassType for CustomObject { +... | + | | fn test_self(self) { + | | ^^^^^^^^^ function or associated item not found in `CustomObject` +... | + | | } + | | ); + | |_- function or associated item `test_self` not found for this struct diff --git a/crates/test-ui/ui/declare_class_invalid_type.rs b/crates/test-ui/ui/declare_class_invalid_type.rs new file mode 100644 index 000000000..8fc23e161 --- /dev/null +++ b/crates/test-ui/ui/declare_class_invalid_type.rs @@ -0,0 +1,35 @@ +use objc2::rc::{Id, Shared}; +use objc2::{declare_class, ClassType}; +use objc2::runtime::NSObject; + +declare_class!( + struct CustomObject; + + unsafe impl ClassType for CustomObject { + type Super = NSObject; + } + + unsafe impl CustomObject { + #[method(test1)] + fn test1() -> Id { + unimplemented!() + } + + #[method(test2)] + fn test2() -> Vec<()> { + unimplemented!() + } + + #[method(test3)] + fn test3(&self, arg: Box) { + unimplemented!() + } + + #[method(test4)] + fn test4(&self, arg: Self) { + unimplemented!() + } + } +); + +fn main() {} diff --git a/crates/test-ui/ui/declare_class_invalid_type.stderr b/crates/test-ui/ui/declare_class_invalid_type.stderr new file mode 100644 index 000000000..caee52c78 --- /dev/null +++ b/crates/test-ui/ui/declare_class_invalid_type.stderr @@ -0,0 +1,119 @@ +error[E0277]: the trait bound `Id: Encode` is not satisfied + --> ui/declare_class_invalid_type.rs + | + | / declare_class!( + | | struct CustomObject; + | | + | | unsafe impl ClassType for CustomObject { +... | + | | } + | | ); + | |_^ 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 `encode::encode::convert_private::Sealed` +note: required by a bound in `EncodeConvert` + --> $WORKSPACE/crates/objc2-encode/src/encode.rs + | + | pub trait EncodeConvert: convert_private::Sealed { + | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `EncodeConvert` + = note: this error originates in the macro `$crate::__declare_class_method_out` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `Vec<()>: Encode` is not satisfied + --> ui/declare_class_invalid_type.rs + | + | / declare_class!( + | | struct CustomObject; + | | + | | unsafe impl ClassType for CustomObject { +... | + | | } + | | ); + | |_^ the trait `Encode` is not implemented for `Vec<()>` + | + = 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 `Vec<()>` to implement `encode::encode::convert_private::Sealed` +note: required by a bound in `EncodeConvert` + --> $WORKSPACE/crates/objc2-encode/src/encode.rs + | + | pub trait EncodeConvert: convert_private::Sealed { + | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `EncodeConvert` + = note: this error originates in the macro `$crate::__declare_class_method_out` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `Box: Encode` is not satisfied + --> ui/declare_class_invalid_type.rs + | + | / declare_class!( + | | struct CustomObject; + | | + | | unsafe impl ClassType for CustomObject { +... | + | | } + | | ); + | |_^ the trait `Encode` is not implemented for `Box` + | + = 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 `Box` to implement `encode::encode::convert_private::Sealed` +note: required by a bound in `EncodeConvert` + --> $WORKSPACE/crates/objc2-encode/src/encode.rs + | + | pub trait EncodeConvert: convert_private::Sealed { + | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `EncodeConvert` + = note: this error originates in the macro `$crate::__fn_args` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `CustomObject: Encode` is not satisfied + --> ui/declare_class_invalid_type.rs + | + | / declare_class!( + | | struct CustomObject; + | | + | | unsafe impl ClassType for CustomObject { +... | + | | } + | | ); + | |_^ the trait `Encode` is not implemented for `CustomObject` + | + = 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 `CustomObject` to implement `encode::encode::convert_private::Sealed` +note: required by a bound in `EncodeConvert` + --> $WORKSPACE/crates/objc2-encode/src/encode.rs + | + | pub trait EncodeConvert: convert_private::Sealed { + | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `EncodeConvert` + = note: this error originates in the macro `$crate::__fn_args` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) From b98689da09d3e437a536219c98aa2363fd1dd726 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Mon, 16 Jan 2023 11:54:57 +0100 Subject: [PATCH 4/6] Refactor declare_class! dealloc method --- crates/objc2/src/declare/ivar_drop.rs | 38 ++++++++++++++++++ crates/objc2/src/macros/declare_class.rs | 51 +++++++++++++----------- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/crates/objc2/src/declare/ivar_drop.rs b/crates/objc2/src/declare/ivar_drop.rs index 012453cc4..1dc4da623 100644 --- a/crates/objc2/src/declare/ivar_drop.rs +++ b/crates/objc2/src/declare/ivar_drop.rs @@ -274,6 +274,28 @@ mod tests { } ); + declare_class!( + #[derive(Debug, PartialEq, Eq)] + struct IvarTesterSubclass { + ivar5: IvarDrop>, + } + + unsafe impl ClassType for IvarTesterSubclass { + type Super = IvarTester; + } + + unsafe impl IvarTesterSubclass { + #[method(init)] + fn init(&mut self) -> Option<&mut Self> { + let this: Option<&mut Self> = unsafe { msg_send![super(self), init] }; + this.map(|this| { + Ivar::write(&mut this.ivar5, Id::into_shared(__RcTestObject::new())); + this + }) + } + } + ); + #[test] fn test_alloc_dealloc() { let expected = __ThreadTestData::current(); @@ -310,6 +332,22 @@ mod tests { expected.assert_current(); } + #[test] + fn test_subclass() { + let mut expected = __ThreadTestData::current(); + + let obj: Id = + unsafe { msg_send_id![IvarTesterSubclass::class(), new] }; + expected.alloc += 5; + expected.init += 5; + expected.assert_current(); + + drop(obj); + expected.release += 5; + expected.dealloc += 5; + expected.assert_current(); + } + #[test] #[cfg_attr(not(debug_assertions), ignore = "only panics in debug mode")] #[should_panic = "an Id in instance variables must always be initialized before use"] diff --git a/crates/objc2/src/macros/declare_class.rs b/crates/objc2/src/macros/declare_class.rs index 276038b0e..df4ada174 100644 --- a/crates/objc2/src/macros/declare_class.rs +++ b/crates/objc2/src/macros/declare_class.rs @@ -403,10 +403,36 @@ macro_rules! declare_class { if false $( || <<$ivar as $crate::declare::IvarType>::Type as $crate::declare::InnerIvarType>::__MAY_DROP )* { + // See the following links for more details: + // - + // - + // - + unsafe extern "C" fn dealloc(this: &mut $for, _cmd: $crate::runtime::Sel) { + // We deallocate each ivar individually instead of + // using e.g. `drop_in_place(this)`, since if the + // type is subclassing another, the type will + // deallocate the superclass' instance variables + // as well! + $( + let ptr: *mut $crate::declare::Ivar<$ivar> = &mut this.$ivar; + // SAFETY: The ivar is valid, and since this + // is the `dealloc` method, we know the ivars + // are never going to be touched again. + unsafe { $crate::__macro_helpers::drop_in_place(ptr) }; + )* + + // Invoke the super class' `dealloc` method. + // + // Note: ARC does this automatically, which means + // most Objective-C code in the wild don't contain + // this; but we _are_ ARC, so we must do this. + unsafe { $crate::msg_send![super(this), dealloc] } + } + unsafe { builder.add_method( $crate::sel!(dealloc), - Self::__objc2_dealloc as unsafe extern "C" fn(_, _), + dealloc as unsafe extern "C" fn(_, _), ); } } @@ -435,29 +461,6 @@ macro_rules! declare_class { } } - impl $for { - // See the following links for more details: - // - - // - - // - - unsafe extern "C" fn __objc2_dealloc(&mut self, _cmd: $crate::runtime::Sel) { - $( - let ptr: *mut $crate::declare::Ivar<$ivar> = &mut self.$ivar; - // SAFETY: The ivar is valid, and since this is the - // `dealloc` method, we know the ivars are never going to - // be touched again. - unsafe { $crate::__macro_helpers::drop_in_place(ptr) }; - )* - - // Invoke the super class' `dealloc` method. - // - // Note: ARC does this automatically, so most Objective-C code - // in the wild don't contain this; but we don't have ARC, so - // we must do this. - unsafe { $crate::msg_send![super(self), dealloc] } - } - } - // Methods $crate::__declare_class_methods! { $($methods)* From ab5b69691b70e81b23ccaf200e4fb8bb52d3275d Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Mon, 16 Jan 2023 10:30:16 +0100 Subject: [PATCH 5/6] Further refactor macro internals --- crates/objc2/CHANGELOG.md | 1 + .../objc2/src/declare/declare_class_tests.rs | 36 ++ .../objc2/src/macros/__attribute_helpers.rs | 335 ++++++------ crates/objc2/src/macros/__rewrite_self_arg.rs | 147 +++--- crates/objc2/src/macros/declare_class.rs | 479 +++++++++--------- crates/objc2/src/macros/extern_methods.rs | 167 +++--- crates/objc2/src/macros/extern_protocol.rs | 156 +++--- .../ui/declare_class_invalid_syntax.stderr | 22 +- .../ui/declare_class_invalid_type.stderr | 8 +- .../ui/extern_methods_invalid_type.stderr | 2 +- .../ui/extern_methods_missing_method.stderr | 2 +- .../ui/extern_methods_selector_twice.stderr | 8 +- .../ui/extern_protocol_class_method.rs | 2 +- crates/test-ui/ui/wrong_optional.stderr | 4 +- 14 files changed, 707 insertions(+), 662 deletions(-) diff --git a/crates/objc2/CHANGELOG.md b/crates/objc2/CHANGELOG.md index 1bf3d47e0..902f57d62 100644 --- a/crates/objc2/CHANGELOG.md +++ b/crates/objc2/CHANGELOG.md @@ -19,6 +19,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Fixed * Allow empty structs in `declare_class!` macro. * Allow using `extern_methods!` without the `ClassType` trait in scope. +* Fixed a few small issues with `declare_class!`. ## 0.3.0-beta.4 - 2022-12-24 diff --git a/crates/objc2/src/declare/declare_class_tests.rs b/crates/objc2/src/declare/declare_class_tests.rs index cbd272377..6e6330322 100644 --- a/crates/objc2/src/declare/declare_class_tests.rs +++ b/crates/objc2/src/declare/declare_class_tests.rs @@ -297,3 +297,39 @@ declare_class!( fn test_all_the_bool() { let _ = DeclareClassAllTheBool::class(); } + +declare_class!( + struct DeclareClassUnreachable; + + unsafe impl ClassType for DeclareClassUnreachable { + type Super = NSObject; + } + + // Ensure none of these warn + unsafe impl DeclareClassUnreachable { + #[method(unreachable)] + fn unreachable(&self) -> bool { + unreachable!() + } + + #[method(unreachableClass)] + fn unreachable_class() -> bool { + unreachable!() + } + + #[method(unreachableVoid)] + fn unreachable_void(&self) { + unreachable!() + } + + #[method(unreachableClassVoid)] + fn unreachable_class_void() { + unreachable!() + } + } +); + +#[test] +fn test_unreachable() { + let _ = DeclareClassUnreachable::class(); +} diff --git a/crates/objc2/src/macros/__attribute_helpers.rs b/crates/objc2/src/macros/__attribute_helpers.rs index f16fd1366..3b84d3f72 100644 --- a/crates/objc2/src/macros/__attribute_helpers.rs +++ b/crates/objc2/src/macros/__attribute_helpers.rs @@ -1,90 +1,3 @@ -/// Remove the `method(...)`, `method_id(...)` and `optional` attributes from -/// a given set of attributes. -/// -/// This is implemented as a tt-muncher, taking the following arguments: -/// - The attributes to be processed -/// - The output that the attributes will be attached to -/// - The attributes that have already been processed (mostly internal) -#[doc(hidden)] -#[macro_export] -macro_rules! __strip_custom_attributes { - // Base case - { - @() // No attributes left to process - @($($output:tt)*) - @($($m_done:tt)*) - } => { - // Output - $($m_done)* - $($output)* - }; - // `method` attribute - { - @( - #[method $($args:tt)*] - $($m_rest:tt)* - ) - @($($output:tt)*) - @($($m_done:tt)*) - } => { - $crate::__strip_custom_attributes! { - @($($m_rest)*) - @($($output)*) - @($($m_done)*) - } - }; - // `method_id` attribute - { - @( - #[method_id $($args:tt)*] - $($m_rest:tt)* - ) - @($($output:tt)*) - @($($m_done:tt)*) - } => { - $crate::__strip_custom_attributes! { - @($($m_rest)*) - @($($output)*) - @($($m_done)*) - } - }; - // `optional` attribute - { - @( - #[optional $($args:tt)*] - $($m_rest:tt)* - ) - @($($output:tt)*) - @($($m_done:tt)*) - } => { - $crate::__strip_custom_attributes! { - @($($m_rest)*) - @($($output)*) - @($($m_done)*) - } - }; - // Other attributes - { - @( - #[$($m_checked:tt)*] - $($m_rest:tt)* - ) - @($($output:tt)*) - @($($m_done:tt)*) - } => { - $crate::__strip_custom_attributes! { - @($($m_rest)*) - @($($output)*) - @( - $($m_done)* - // The attribute is appended to the current set, since we've - // been consuming the attributes from the front. - #[$($m_checked)*] - ) - } - }; -} - /// Parse the given attributes, and gate the output on any `cfg` attributes /// that were present in the set. /// @@ -134,181 +47,257 @@ macro_rules! __extract_and_apply_cfg_attributes { }; } -/// Extract a `#[method(...)]` or `#[method_id(...)]` and the `#[optional]` +/// Extract `#[method(...)]` or `#[method_id(...)]` and the `#[optional]` /// attribute, and send it to another macro. /// /// This will ensure that there is one and only one of the method attributes /// present. /// /// This is implemented as a tt-muncher, taking the following arguments: -/// - The attributes to be processed -/// - The output macro that will be called -/// - Any extra arguments given to the output macro -/// - The `method`/`method_id` attribute that has already been processed, if -/// any (mostly internal) -/// - The `optional` attribute that has already been processed, if any (mostly -/// internal) +/// - The attributes to parse. +/// - The function name, for better UI if an error occurs. +/// - The output macro that will be called. +/// - Any extra arguments given to the output macro. /// /// And will call the output macro with the given arguments, along with the /// following extra arguments: /// - The `method` or `method_id` attribute. /// - The `optional` attribute, if any. +/// - The rest of the attributes. #[doc(hidden)] #[macro_export] macro_rules! __extract_custom_attributes { + { + ($($m:tt)*) + ($name:ident) + + ($out_macro:path) + $($macro_args:tt)* + } => { + $crate::__extract_custom_attributes_inner! { + ($($m)*) + // No already parsed attributes + () + () + () + ($name) + + ($out_macro) + $($macro_args)* + } + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! __extract_custom_attributes_inner { // No method/method_id attribute found { // No attributes left to process - @() - @($out_macro:path) - @($($macro_args:tt)*) + () // And we found no `method` or `method_id` attributes - @() - @($($m_optional:tt)*) + () + ($($m_optional:tt)*) + ($($m_checked:tt)*) + ($name:ident) + + ($out_macro:path) + $($macro_args:tt)* } => { - compile_error!("must specify the desired selector using `#[method(...)]` or `#[method_id(...)]`") + fn $name() { + compile_error!("must specify the desired selector using `#[method(...)]` or `#[method_id(...)]`") + } }; + // Base case { // No attributes left to process - @() - @($out_macro:path) - @($($macro_args:tt)*) + () // And we found a `method` or `method_id` attribute - @($($m_method:tt)*) - @($($m_optional:tt)*) - } => {{ + ($($m_method:tt)*) + ($($m_optional:tt)*) + ($($m_checked:tt)*) + ($name:ident) + + ($out_macro:path) + $($macro_args:tt)* + } => { // Output $out_macro! { $($macro_args)* // Append attributes to the end of the macro arguments - @($($m_method)*) - @($($m_optional)*) + ($($m_method)*) + ($($m_optional)*) + ($($m_checked)*) } - }}; + }; // `method` attribute { - @( + ( #[method($($args:tt)*)] $($rest:tt)* ) - @($out_macro:path) - @($($macro_args:tt)*) // If no existing `method` nor `method_id` attributes exist - @() - @($($m_optional:tt)*) + () + ($($m_optional:tt)*) + ($($m_checked:tt)*) + ($name:ident) + + ($out_macro:path) + $($macro_args:tt)* } => { - $crate::__extract_custom_attributes! { - @($($rest)*) - @($out_macro) - @($($macro_args)*) + $crate::__extract_custom_attributes_inner! { + ($($rest)*) // Add method attribute - @(#[method($($args)*)]) - @($($m_optional)*) + (#[method($($args)*)]) + ($($m_optional)*) + ($($m_checked)*) + ($name) + + ($out_macro) + $($macro_args)* } }; // Duplicate `method` attributes { - @( + ( #[method($($args:tt)*)] $($rest:tt)* ) - @($out_macro:path) - @($($macro_args:tt)*) - @($($m_method:tt)*) - @($($m_optional:tt)*) + ($($m_method:tt)*) + ($($m_optional:tt)*) + ($($m_checked:tt)*) + ($name:ident) + + ($out_macro:path) + $($macro_args:tt)* } => { - compile_error!("cannot specify the `method`/`method_id` attribute twice") + fn $name() { + compile_error!("cannot specify the `method`/`method_id` attribute twice") + } }; // `method_id` attribute { - @( + ( #[method_id($($args:tt)*)] $($rest:tt)* ) - @($out_macro:path) - @($($macro_args:tt)*) // If no existing `method` nor `method_id` attributes exist - @() - @($($m_optional:tt)*) + () + ($($m_optional:tt)*) + ($($m_checked:tt)*) + ($name:ident) + + ($out_macro:path) + $($macro_args:tt)* } => { - $crate::__extract_custom_attributes! { - @($($rest)*) - @($out_macro) - @($($macro_args)*) + $crate::__extract_custom_attributes_inner! { + ($($rest)*) // Add method_id attribute - @(#[method_id($($args)*)]) - @($($m_optional)*) + (#[method_id($($args)*)]) + ($($m_optional)*) + ($($m_checked)*) + ($name) + + ($out_macro) + $($macro_args)* } }; // Duplicate `method` attributes { - @( + ( #[method_id($($args:tt)*)] $($rest:tt)* ) - @($out_macro:path) - @($($macro_args:tt)*) - @($($m_method:tt)*) - @($($m_optional:tt)*) + ($($m_method:tt)*) + ($($m_optional:tt)*) + ($($m_checked:tt)*) + ($name:ident) + + ($out_macro:path) + $($macro_args:tt)* } => { - compile_error!("cannot specify the `method`/`method_id` attribute twice") + fn $name() { + compile_error!("cannot specify the `method`/`method_id` attribute twice") + } }; // `optional` attribute { - @( + ( #[optional] $($rest:tt)* ) - @($out_macro:path) - @($($macro_args:tt)*) - @($($m_method:tt)*) + ($($m_method:tt)*) // If no existing `optional` attributes exist - @() + () + ($($m_checked:tt)*) + ($name:ident) + + ($out_macro:path) + $($macro_args:tt)* } => { - $crate::__extract_custom_attributes! { - @($($rest)*) - @($out_macro) - @($($macro_args)*) - @($($m_method)*) + $crate::__extract_custom_attributes_inner! { + ($($rest)*) + ($($m_method)*) // Add optional attribute - @(#[optional]) + (#[optional]) + ($($m_checked)*) + ($name) + + ($out_macro) + $($macro_args)* } }; // Duplicate `optional` attributes { - @( + ( #[optional] $($rest:tt)* ) - @($out_macro:path) - @($($macro_args:tt)*) - @($($m_method:tt)*) - @($($m_optional:tt)*) + ($($m_method:tt)*) + ($($m_optional:tt)*) + ($($m_checked:tt)*) + ($name:ident) + + ($out_macro:path) + $($macro_args:tt)* } => { - compile_error!("cannot specify the `optional` attribute twice") + fn $name() { + compile_error!("cannot specify the `optional` attribute twice") + } }; // Other attributes { - @( - #[$($m_checked:tt)*] + ( + #[$($checked:tt)*] $($rest:tt)* ) - @($out_macro:path) - @($($macro_args:tt)*) - @($($m_method:tt)*) - @($($m_optional:tt)*) + ($($m_method:tt)*) + ($($m_optional:tt)*) + ($($m_checked:tt)*) + ($name:ident) + + ($out_macro:path) + $($macro_args:tt)* } => { - $crate::__extract_custom_attributes! { - @($($rest)*) - @($out_macro) - @($($macro_args)*) - @($($m_method)*) - @($($m_optional)*) + $crate::__extract_custom_attributes_inner! { + ($($rest)*) + ($($m_method)*) + ($($m_optional)*) + ( + $($m_checked)* + // The attribute is appended to the current set, since we've + // been consuming the attributes from the front. + #[$($checked)*] + ) + ($name) + + ($out_macro) + $($macro_args)* } }; } diff --git a/crates/objc2/src/macros/__rewrite_self_arg.rs b/crates/objc2/src/macros/__rewrite_self_arg.rs index d91a94424..5d34bba97 100644 --- a/crates/objc2/src/macros/__rewrite_self_arg.rs +++ b/crates/objc2/src/macros/__rewrite_self_arg.rs @@ -1,19 +1,28 @@ +/// Detect instance vs. class method. +/// +/// Will add: +/// ```ignore +/// (builder method) +/// (receiver) +/// (args_prefix*) +/// (args_rest*) +/// ``` #[doc(hidden)] #[macro_export] macro_rules! __rewrite_self_arg { { - ($out_macro:path) ($($args:tt)*) + ($out_macro:path) $($macro_args:tt)* } => { $crate::__rewrite_self_arg_inner! { - ($out_macro) // Duplicate args out so that we can match on `self`, while still // using it as a function argument ($($args)*) ($($args)*) + ($out_macro) $($macro_args)* } }; @@ -24,71 +33,79 @@ macro_rules! __rewrite_self_arg { macro_rules! __rewrite_self_arg_inner { // Instance method { - ($out_macro:path) - (&self $($__rest_args:tt)*) - (&$self:ident $(, $($rest:tt)*)?) + (&self $($__args_rest:tt)*) + (&$self:ident $(, $($args_rest:tt)*)?) + ($out_macro:path) $($macro_args:tt)* } => { $out_macro! { $($macro_args)* - @(instance_method) - @( - $self: &Self, + + (add_method) + ($self) + ( + &$self, _: $crate::runtime::Sel, ) - @($($($rest)*)?) + ($($($args_rest)*)?) } }; { - ($out_macro:path) - (&mut self $($__rest_args:tt)*) - (&mut $self:ident $(, $($rest:tt)*)?) + (&mut self $($__args_rest:tt)*) + (&mut $self:ident $(, $($args_rest:tt)*)?) + ($out_macro:path) $($macro_args:tt)* } => { $out_macro! { $($macro_args)* - @(instance_method) - @( - $self: &mut Self, + + (add_method) + ($self) + ( + &mut $self, _: $crate::runtime::Sel, ) - @($($($rest)*)?) + ($($($args_rest)*)?) } }; { - ($out_macro:path) - (self: $__self_ty:ty $(, $($__rest_args:tt)*)?) - ($self:ident: $self_ty:ty $(, $($rest:tt)*)?) + (self: $__self_ty:ty $(, $($__args_rest:tt)*)?) + ($self:ident: $self_ty:ty $(, $($args_rest:tt)*)?) + ($out_macro:path) $($macro_args:tt)* } => { $out_macro! { $($macro_args)* - @(instance_method) - @( + + (add_method) + ($self) + ( $self: $self_ty, _: $crate::runtime::Sel, ) - @($($($rest)*)?) + ($($($args_rest)*)?) } }; { - ($out_macro:path) - (mut self: $__self_ty:ty $(, $($__rest_args:tt)*)?) - (mut $self:ident: $self_ty:ty $(, $($rest:tt)*)?) + (mut self: $__self_ty:ty $(, $($__args_rest:tt)*)?) + ($mut:ident $self:ident: $self_ty:ty $(, $($args_rest:tt)*)?) + ($out_macro:path) $($macro_args:tt)* } => { $out_macro! { $($macro_args)* - @(instance_method) - @( - mut $self: $self_ty, + + (add_method) + ($self) + ( + $mut $self: $self_ty, _: $crate::runtime::Sel, ) - @($($($rest)*)?) + ($($($args_rest)*)?) } }; @@ -96,90 +113,100 @@ macro_rules! __rewrite_self_arg_inner { // Workaround for arbitary self types being unstable // https://doc.rust-lang.org/nightly/unstable-book/language-features/arbitrary-self-types.html { - ($out_macro:path) - (mut this: $__self_ty:ty $(, $($__rest_args:tt)*)?) - (mut $this:ident: $this_ty:ty $(, $($rest:tt)*)?) + (mut this: $__self_ty:ty $(, $($__args_rest:tt)*)?) + ($mut:ident $this:ident: $this_ty:ty $(, $($args_rest:tt)*)?) + ($out_macro:path) $($macro_args:tt)* } => { $out_macro! { $($macro_args)* - @(instance_method) - @( - mut $this: $this_ty, + + (add_method) + ($this) + ( + $mut $this: $this_ty, _: $crate::runtime::Sel, ) - @($($($rest)*)?) + ($($($args_rest)*)?) } }; { - ($out_macro:path) - (this: $__self_ty:ty $(, $($__rest_args:tt)*)?) - ($this:ident: $this_ty:ty $(, $($rest:tt)*)?) + (this: $__self_ty:ty $(, $($__args_rest:tt)*)?) + ($this:ident: $this_ty:ty $(, $($args_rest:tt)*)?) + ($out_macro:path) $($macro_args:tt)* } => { $out_macro! { $($macro_args)* - @(instance_method) - @( + + (add_method) + ($this) + ( $this: $this_ty, _: $crate::runtime::Sel, ) - @($($($rest)*)?) + ($($($args_rest)*)?) } }; { - ($out_macro:path) - (mut _this: $__self_ty:ty $(, $($__rest_args:tt)*)?) - (mut $this:ident: $this_ty:ty $(, $($rest:tt)*)?) + (mut _this: $__self_ty:ty $(, $($__args_rest:tt)*)?) + ($mut:ident $this:ident: $this_ty:ty $(, $($args_rest:tt)*)?) + ($out_macro:path) $($macro_args:tt)* } => { $out_macro! { $($macro_args)* - @(instance_method) - @( - mut $this: $this_ty, + + (add_method) + ($this) + ( + $mut $this: $this_ty, _: $crate::runtime::Sel, ) - @($($($rest)*)?) + ($($($args_rest)*)?) } }; { - ($out_macro:path) - (_this: $__self_ty:ty $(, $($__rest_args:tt)*)?) - ($this:ident: $this_ty:ty $(, $($rest:tt)*)?) + (_this: $__self_ty:ty $(, $($__args_rest:tt)*)?) + ($this:ident: $this_ty:ty $(, $($args_rest:tt)*)?) + ($out_macro:path) $($macro_args:tt)* } => { $out_macro! { $($macro_args)* - @(instance_method) - @( + + (add_method) + ($this) + ( $this: $this_ty, _: $crate::runtime::Sel, ) - @($($($rest)*)?) + ($($($args_rest)*)?) } }; // Class method { - ($out_macro:path) ($($__args:tt)*) - ($($args:tt)*) + ($($args_rest:tt)*) + ($out_macro:path) $($macro_args:tt)* } => { $out_macro! { $($macro_args)* - @(class_method) - @( + + (add_class_method) + (::class()) + ( _: &$crate::runtime::Class, _: $crate::runtime::Sel, ) - @($($args)*) + ($($args_rest)*) } }; } diff --git a/crates/objc2/src/macros/declare_class.rs b/crates/objc2/src/macros/declare_class.rs index df4ada174..11aeab07d 100644 --- a/crates/objc2/src/macros/declare_class.rs +++ b/crates/objc2/src/macros/declare_class.rs @@ -386,8 +386,11 @@ macro_rules! declare_class { static REGISTER_CLASS: $crate::__macro_helpers::Once = $crate::__macro_helpers::Once::new(); REGISTER_CLASS.call_once(|| { - let superclass = <$superclass as $crate::ClassType>::class(); - let mut builder = $crate::declare::ClassBuilder::new(::NAME, superclass).unwrap_or_else(|| { + let __objc2_superclass = <$superclass as $crate::ClassType>::class(); + let mut __objc2_builder = $crate::declare::ClassBuilder::new( + ::NAME, + __objc2_superclass, + ).unwrap_or_else(|| { $crate::__macro_helpers::panic!( "could not create new class {}. Perhaps a class with that name already exists?", ::NAME, @@ -396,7 +399,7 @@ macro_rules! declare_class { // Ivars $( - builder.add_static_ivar::<$ivar>(); + __objc2_builder.add_static_ivar::<$ivar>(); )* // Check whether we need to add a `dealloc` method @@ -407,18 +410,18 @@ macro_rules! declare_class { // - // - // - - unsafe extern "C" fn dealloc(this: &mut $for, _cmd: $crate::runtime::Sel) { + unsafe extern "C" fn __objc2_dealloc(__objc2_self: &mut $for, _: $crate::runtime::Sel) { // We deallocate each ivar individually instead of - // using e.g. `drop_in_place(this)`, since if the - // type is subclassing another, the type will - // deallocate the superclass' instance variables - // as well! + // using e.g. `drop_in_place(__objc2_self)`, since + // if the type is subclassing another, the type + // will deallocate the superclass' instance + // variables as well! $( - let ptr: *mut $crate::declare::Ivar<$ivar> = &mut this.$ivar; + let __objc2_ptr: *mut $crate::declare::Ivar<$ivar> = &mut __objc2_self.$ivar; // SAFETY: The ivar is valid, and since this // is the `dealloc` method, we know the ivars // are never going to be touched again. - unsafe { $crate::__macro_helpers::drop_in_place(ptr) }; + unsafe { $crate::__macro_helpers::drop_in_place(__objc2_ptr) }; )* // Invoke the super class' `dealloc` method. @@ -426,24 +429,24 @@ macro_rules! declare_class { // Note: ARC does this automatically, which means // most Objective-C code in the wild don't contain // this; but we _are_ ARC, so we must do this. - unsafe { $crate::msg_send![super(this), dealloc] } + unsafe { $crate::msg_send![super(__objc2_self), dealloc] } } unsafe { - builder.add_method( + __objc2_builder.add_method( $crate::sel!(dealloc), - dealloc as unsafe extern "C" fn(_, _), + __objc2_dealloc as unsafe extern "C" fn(_, _), ); } } // Implement protocols and methods $crate::__declare_class_register_methods! { - @(builder) + (__objc2_builder) $($methods)* } - let _cls = builder.register(); + let _cls = __objc2_builder.register(); }); // We just registered the class, so it should be available @@ -515,8 +518,9 @@ macro_rules! __declare_class_methods { $(#[$m])* impl $for { $crate::__declare_class_rewrite_methods! { - @($crate::__declare_class_method_out) - @() + ($crate::__declare_class_method_out) + () + $($methods)* } } @@ -537,8 +541,9 @@ macro_rules! __declare_class_methods { $(#[$m])* impl $for { $crate::__declare_class_rewrite_methods! { - @($crate::__declare_class_method_out) - @() + ($crate::__declare_class_method_out) + () + $($methods)* } } @@ -553,10 +558,13 @@ macro_rules! __declare_class_methods { #[macro_export] macro_rules! __declare_class_register_methods { // Base-case - (@($builder:ident)) => {}; + ( + ($builder:ident) + ) => {}; + // With protocol ( - @($builder:ident) + ($builder:ident) $(#[$($m:tt)*])* unsafe impl ConformsTo<$protocol:ty> for $for:ty { @@ -570,7 +578,7 @@ macro_rules! __declare_class_register_methods { @( // Implement protocol #[allow(unused_mut)] - let mut protocol_builder = $builder.__add_protocol_methods( + let mut __objc2_protocol_builder = $builder.__add_protocol_methods( <$protocol as $crate::ProtocolType>::protocol() ); @@ -581,26 +589,27 @@ macro_rules! __declare_class_register_methods { // SAFETY: Upheld by caller unsafe { $crate::__declare_class_rewrite_methods! { - @($crate::__declare_class_register_out) - @(protocol_builder) + ($crate::__declare_class_register_out) + (__objc2_protocol_builder) $($methods)* } } // Finished declaring protocol; get error message if any - protocol_builder.__finish(); + __objc2_protocol_builder.__finish(); ) } $crate::__declare_class_register_methods! { - @($builder) + ($builder) $($rest)* } }; + // Without protocol ( - @($builder:ident) + ($builder:ident) $(#[$($m:tt)*])* unsafe impl $for:ty { @@ -619,8 +628,8 @@ macro_rules! __declare_class_register_methods { // SAFETY: Upheld by caller unsafe { $crate::__declare_class_rewrite_methods! { - @($crate::__declare_class_register_out) - @($builder) + ($crate::__declare_class_register_out) + ($builder) $($methods)* } @@ -629,7 +638,7 @@ macro_rules! __declare_class_register_methods { } $crate::__declare_class_register_methods! { - @($builder) + ($builder) $($rest)* } }; @@ -639,14 +648,14 @@ macro_rules! __declare_class_register_methods { #[macro_export] macro_rules! __declare_class_rewrite_methods { { - @($out_macro:path) - @($($macro_arguments:tt)*) + ($out_macro:path) + ($($macro_arg:tt)*) } => {}; // Unsafe variant { - @($out_macro:path) - @($($macro_arguments:tt)*) + ($out_macro:path) + ($($macro_arg:tt)*) $(#[$($m:tt)*])* unsafe fn $name:ident($($args:tt)*) $(-> $ret:ty)? $body:block @@ -654,25 +663,23 @@ macro_rules! __declare_class_rewrite_methods { $($rest:tt)* } => { $crate::__rewrite_self_arg! { - ($out_macro) ($($args)*) - // Split the function into parts, and send the arguments down to - // be used later on - @($($macro_arguments)*) - @($(#[$($m)*])*) - @(unsafe extern "C") - @($name) - @($($ret)?) - @($body) - // Will add @(kind) - // Will add @(args_start) - // Will add @(args_rest) + ($crate::__extract_custom_attributes) + ($(#[$($m)*])*) + ($name) + + ($out_macro) + ($($macro_arg)*) + (unsafe) + ($name) + ($($ret)?) + ($body) } $crate::__declare_class_rewrite_methods! { - @($out_macro) - @($($macro_arguments)*) + ($out_macro) + ($($macro_arg)*) $($rest)* } @@ -680,8 +687,8 @@ macro_rules! __declare_class_rewrite_methods { // Safe variant { - @($out_macro:path) - @($($macro_arguments:tt)*) + ($out_macro:path) + ($($macro_arg:tt)*) $(#[$($m:tt)*])* fn $name:ident($($args:tt)*) $(-> $ret:ty)? $body:block @@ -689,22 +696,23 @@ macro_rules! __declare_class_rewrite_methods { $($rest:tt)* } => { $crate::__rewrite_self_arg! { - ($out_macro) ($($args)*) - @($($macro_arguments)*) - @($(#[$($m)*])*) - @(extern "C") - @($name) - @($($ret)?) - @($body) + ($crate::__extract_custom_attributes) + ($(#[$($m)*])*) + ($name) - // Same as above + ($out_macro) + ($($macro_arg)*) + () + ($name) + ($($ret)?) + ($body) } $crate::__declare_class_rewrite_methods! { - @($out_macro) - @($($macro_arguments)*) + ($out_macro) + ($($macro_arg)*) $($rest)* } @@ -715,226 +723,211 @@ macro_rules! __declare_class_rewrite_methods { #[macro_export] macro_rules! __declare_class_method_out { { - @() // No arguments needed - @($(#[$($m:tt)*])*) - @($($qualifiers:tt)*) - @($name:ident) - @($($ret:ty)?) - @($($body:tt)*) - @($($_kind:tt)*) - @($($args_start:tt)*) - @($($args_rest:tt)*) + () + ($($qualifiers:tt)*) + ($name:ident) + ($($ret:ty)?) + ($body:block) + + ($builder_method:ident) + ($receiver:expr) + ($($args_prefix:tt)*) + ($($args_rest:tt)*) + + ($($m_method:tt)*) + ($($m_optional:tt)*) + ($($m_checked:tt)*) } => { - $crate::__fn_args! { - ($crate::__declare_class_method_out) - ($($args_rest)*,) + $crate::__declare_class_rewrite_args! { + ($($args_rest)*) () () - @inner - @($(#[$($m)*])*) - @($($qualifiers)*) - @($name) - @($($ret)?) - @($($body)*) - @($($_kind)*) - @($($args_start)*) - // Will add @(args_converted) - // Will add @(body_prefix) - } - }; - // No return type - { - @inner - @($(#[$($m:tt)*])*) - @($($qualifiers:tt)*) - @($name:ident) - @() - @($($body:tt)*) - @($($_kind:tt)*) - @($($args_start:tt)*) - @($($args_converted:tt)*) - @($($body_prefix:tt)*) - } => { - $crate::__strip_custom_attributes! { - @($(#[$($m)*])*) - @($($qualifiers)* fn $name($($args_start)* $($args_converted)*) { - $($body_prefix)* - $($body)* - }) - @() - } - }; + ($crate::__declare_class_method_out_inner) - // With return type - { - @inner - @($(#[$($m:tt)*])*) - @($($qualifiers:tt)*) - @($name:ident) - @($ret:ty) - @($($body:tt)*) - @($($_kind:tt)*) - @($($args_start:tt)*) - @($($args_converted:tt)*) - @($($body_prefix:tt)*) - } => { - $crate::__strip_custom_attributes! { - @($(#[$($m)*])*) - @($($qualifiers)* fn $name($($args_start)* $($args_converted)*) -> <$ret as $crate::encode::EncodeConvert>::__Inner { - $($body_prefix)* - <$ret as $crate::encode::EncodeConvert>::__into_inner($($body)*) - }) - @() + ($($qualifiers)*) + ($name) + ($($ret)?) + ($body) + + ($builder_method) + ($receiver) + ($($args_prefix)*) + + ($($m_method)*) + ($($m_optional)*) + ($($m_checked)*) } }; } #[doc(hidden)] #[macro_export] -macro_rules! __fn_args { - // Ignore `_` +macro_rules! __declare_class_rewrite_args { + // Convert _ { - ($out_macro:path) - (_ : $param_ty:ty, $($rest:tt)*) + (_ : $param_ty:ty $(, $($rest_args:tt)*)?) ($($args_converted:tt)*) ($($body_prefix:tt)*) + + ($out_macro:path) $($macro_args:tt)* } => { - $crate::__fn_args! { - ($out_macro) - ($($rest)*) + $crate::__declare_class_rewrite_args! { + ($($($rest_args)*)?) ($($args_converted)* _ : <$param_ty as $crate::encode::EncodeConvert>::__Inner,) ($($body_prefix)*) + + ($out_macro) $($macro_args)* } }; // Convert mut { - ($out_macro:path) - (mut $param:ident : $param_ty:ty, $($rest:tt)*) + (mut $param:ident : $param_ty:ty $(, $($rest_args:tt)*)?) ($($args_converted:tt)*) ($($body_prefix:tt)*) + + ($out_macro:path) $($macro_args:tt)* } => { - $crate::__fn_args! { - ($out_macro) - ($($rest)*) + $crate::__declare_class_rewrite_args! { + ($($($rest_args)*)?) ($($args_converted)* $param : <$param_ty as $crate::encode::EncodeConvert>::__Inner,) ( $($body_prefix)* let mut $param = <$param_ty as $crate::encode::EncodeConvert>::__from_inner($param); ) + + ($out_macro) $($macro_args)* } }; // Convert { - ($out_macro:path) - ($param:ident : $param_ty:ty, $($rest:tt)*) + ($param:ident : $param_ty:ty $(, $($rest_args:tt)*)?) ($($args_converted:tt)*) ($($body_prefix:tt)*) + + ($out_macro:path) $($macro_args:tt)* } => { - $crate::__fn_args! { - ($out_macro) - ($($rest)*) + $crate::__declare_class_rewrite_args! { + ($($($rest_args)*)?) ($($args_converted)* $param : <$param_ty as $crate::encode::EncodeConvert>::__Inner,) ( $($body_prefix)* let $param = <$param_ty as $crate::encode::EncodeConvert>::__from_inner($param); ) + + ($out_macro) $($macro_args)* } }; // Output result { - ($out_macro:path) - ($(,)*) + () ($($args_converted:tt)*) ($($body_prefix:tt)*) + + ($out_macro:path) $($macro_args:tt)* } => { $out_macro! { $($macro_args)* - @($($args_converted)*) - @($($body_prefix)*) + + ($($args_converted)*) + ($($body_prefix)*) } }; } #[doc(hidden)] #[macro_export] -macro_rules! __declare_class_register_out { - // Class method +macro_rules! __declare_class_method_out_inner { { - @($builder:ident) - @($(#[$($m:tt)*])*) - @($($qualifiers:tt)*) - @($name:ident) - @($($_ret:tt)*) - @($($_body:tt)*) - @(class_method) - @($($args_start:tt)*) - @($($args_rest:tt)*) + ($($qualifiers:tt)*) + ($name:ident) + () + ($body:block) + + ($__builder_method:ident) + ($__receiver:expr) + ($($args_prefix:tt)*) + + ($($__m_method:tt)*) + ($($__m_optional:tt)*) + ($($m_checked:tt)*) + + ($($args_converted:tt)*) + ($($body_prefix:tt)*) } => { - $crate::__extract_and_apply_cfg_attributes! { - @($(#[$($m)*])*) - @( - $builder.add_class_method( - $crate::__extract_custom_attributes! { - @($(#[$($m)*])*) - @($crate::__declare_class_register_out) - @( - @call_sel - // Macro will add: - // @(method attribute) - // @(optional attribute) - ) - @() - @() - }, - Self::$name as $crate::__fn_ptr! { - @($($qualifiers)*) - @(_, _,) - $($args_rest)* - }, - ); - ) + $($m_checked)* + $($qualifiers)* extern "C" fn $name( + $($args_prefix)* + $($args_converted)* + ) { + $($body_prefix)* + $body } }; + { + ($($qualifiers:tt)*) + ($name:ident) + ($ret:ty) + ($body:block) + + ($__builder_method:ident) + ($__receiver:expr) + ($($args_prefix:tt)*) + + ($($__m_method:tt)*) + ($($__m_optional:tt)*) + ($($m_checked:tt)*) - // Instance method + ($($args_converted:tt)*) + ($($body_prefix:tt)*) + } => { + $($m_checked)* + $($qualifiers)* extern "C" fn $name( + $($args_prefix)* + $($args_converted)* + ) -> <$ret as $crate::encode::EncodeConvert>::__Inner { + $($body_prefix)* + let __objc2_result = $body; + #[allow(unreachable_code)] + <$ret as $crate::encode::EncodeConvert>::__into_inner(__objc2_result) + } + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! __declare_class_register_out { { - @($builder:ident) - @($(#[$($m:tt)*])*) - @($($qualifiers:tt)*) - @($name:ident) - @($($_ret:tt)*) - @($($_body:tt)*) - @(instance_method) - @($($args_start:tt)*) - @($($args_rest:tt)*) + ($builder:ident) + ($($qualifiers:tt)*) + ($name:ident) + ($($__ret:ty)?) + ($__body:block) + + ($builder_method:ident) + ($__receiver:expr) + ($($__args_prefix:tt)*) + ($($args_rest:tt)*) + + (#[method($($sel:tt)*)]) + () // No optional + ($($m_checked:tt)*) } => { $crate::__extract_and_apply_cfg_attributes! { - @($(#[$($m)*])*) + @($($m_checked)*) @( - $builder.add_method( - $crate::__extract_custom_attributes! { - @($(#[$($m)*])*) - @($crate::__declare_class_register_out) - @( - @call_sel - // Macro will add: - // @(method attribute) - // @(optional attribute) - ) - @() - @() - }, + $builder.$builder_method( + $crate::sel!($($sel)*), Self::$name as $crate::__fn_ptr! { - @($($qualifiers)*) - @(_, _,) + ($($qualifiers)*) + (_, _,) $($args_rest)* }, ); @@ -943,25 +936,39 @@ macro_rules! __declare_class_register_out { }; { - @call_sel - @(#[method($($sel:tt)*)]) - @() - } => { - $crate::sel!($($sel)*) - }; - - { - @call_sel - @(#[method($($sel:tt)*)]) - @($($m_optional:tt)*) + ($builder:ident) + ($($qualifiers:tt)*) + ($name:ident) + ($($__ret:ty)?) + ($__body:block) + + ($builder_method:ident) + ($__receiver:expr) + ($($__args_prefix:tt)*) + ($($args_rest:tt)*) + + (#[method($($sel:tt)*)]) + ($($m_optional:tt)*) + ($($m_checked:tt)*) } => { compile_error!("`#[optional]` is only supported in `extern_protocol!`") }; { - @call_sel - @(#[method_id($($sel:tt)*)]) - @($($m_optional:tt)*) + ($builder:ident) + ($($qualifiers:tt)*) + ($name:ident) + ($($__ret:ty)?) + ($__body:block) + + ($builder_method:ident) + ($__receiver:expr) + ($($__args_prefix:tt)*) + ($($args_rest:tt)*) + + (#[method_id($($sel:tt)*)]) + ($($m_optional:tt)*) + ($($m_checked:tt)*) } => { compile_error!("`#[method_id(...)]` is not supported in `declare_class!` yet") }; @@ -972,42 +979,42 @@ macro_rules! __declare_class_register_out { #[macro_export] macro_rules! __fn_ptr { ( - @($($qualifiers:tt)*) - @($($output:tt)*) + ($($qualifiers:tt)*) + ($($output:tt)*) $(,)? ) => { - $($qualifiers)* fn($($output)*) -> _ + $($qualifiers)* extern "C" fn($($output)*) -> _ }; ( - @($($qualifiers:tt)*) - @($($output:tt)*) + ($($qualifiers:tt)*) + ($($output:tt)*) _ : $param_ty:ty $(, $($rest:tt)*)? ) => { $crate::__fn_ptr! { - @($($qualifiers)*) - @($($output)* _,) + ($($qualifiers)*) + ($($output)* _,) $($($rest)*)? } }; ( - @($($qualifiers:tt)*) - @($($output:tt)*) + ($($qualifiers:tt)*) + ($($output:tt)*) mut $param:ident : $param_ty:ty $(, $($rest:tt)*)? ) => { $crate::__fn_ptr! { - @($($qualifiers)*) - @($($output)* _,) + ($($qualifiers)*) + ($($output)* _,) $($($rest)*)? } }; ( - @($($qualifiers:tt)*) - @($($output:tt)*) + ($($qualifiers:tt)*) + ($($output:tt)*) $param:ident : $param_ty:ty $(, $($rest:tt)*)? ) => { $crate::__fn_ptr! { - @($($qualifiers)*) - @($($output)* _,) + ($($qualifiers)*) + ($($output)* _,) $($($rest)*)? } }; diff --git a/crates/objc2/src/macros/extern_methods.rs b/crates/objc2/src/macros/extern_methods.rs index 0325737b1..dc0e39bd7 100644 --- a/crates/objc2/src/macros/extern_methods.rs +++ b/crates/objc2/src/macros/extern_methods.rs @@ -217,15 +217,15 @@ macro_rules! __extern_methods_rewrite_methods { $($rest:tt)* } => { - // Detect instance vs. class method. $crate::__rewrite_self_arg! { - ($crate::__extern_methods_method_out) ($($args)*) - @($(#[$($m)*])*) - @($v unsafe fn $name($($args)*) $(-> $ret)?) - // Will add @(kind) - // Will add @(args_start) - // Will add @(args_rest) + + ($crate::__extract_custom_attributes) + ($(#[$($m)*])*) + ($name) + + ($crate::__extern_methods_method_out) + ($v unsafe fn $name($($args)*) $(-> $ret)?) } $crate::__extern_methods_rewrite_methods! { @@ -241,10 +241,14 @@ macro_rules! __extern_methods_rewrite_methods { $($rest:tt)* } => { $crate::__rewrite_self_arg! { - ($crate::__extern_methods_method_out) ($($args)*) - @($(#[$($m)*])*) - @($v fn $name($($args)*) $(-> $ret)?) + + ($crate::__extract_custom_attributes) + ($(#[$($m)*])*) + ($name) + + ($crate::__extern_methods_method_out) + ($v fn $name($($args)*) $(-> $ret)?) } $crate::__extern_methods_rewrite_methods! { @@ -270,110 +274,81 @@ macro_rules! __extern_methods_rewrite_methods { #[doc(hidden)] #[macro_export] macro_rules! __extern_methods_method_out { - { - @($(#[$($m:tt)*])*) - @($($function_start:tt)*) - @($($kind:tt)*) - @($($args_start:tt)*) - @($($args_rest:tt)*) - } => { - $crate::__strip_custom_attributes! { - @($(#[$($m)*])*) - @($($function_start)* { - #[allow(unused_unsafe)] - unsafe { - $crate::__extract_custom_attributes! { - @($(#[$($m)*])*) - @($crate::__extern_methods_unsafe_method_body) - @( - @($crate::__extern_methods_get_receiver!( - @($($kind)*) - @($($args_start)*) - )) - @($($args_rest)*) - // Macro will add: - // @(method attribute) - // @(optional attribute) - ) - @() - @() - } - } - }) - @() - } - }; -} - -#[doc(hidden)] -#[macro_export] -macro_rules! __extern_methods_unsafe_method_body { // #[method(...)] { - @($receiver:expr) - @($($args_rest:tt)*) - @(#[method($($sel:tt)*)]) - @() // No `optional` + ($($function_start:tt)*) + + ($__builder_method:ident) + ($receiver:expr) + ($($__args_prefix:tt)*) + ($($args_rest:tt)*) + + (#[method($($sel:tt)*)]) + () // No `optional` + ($($m_checked:tt)*) } => { - $crate::__method_msg_send! { - ($receiver) - ($($sel)*) - ($($args_rest)*) + $($m_checked)* + $($function_start)* { + #[allow(unused_unsafe)] + unsafe { + $crate::__method_msg_send! { + ($receiver) + ($($sel)*) + ($($args_rest)*) - () - () + () + () + } + } } }; // #[method_id(...)] { - @($receiver:expr) - @($($args_rest:tt)*) - @(#[method_id($($sel:tt)*)]) - @() // No `optional` + ($($function_start:tt)*) + + ($__builder_method:ident) + ($receiver:expr) + ($($__args_prefix:tt)*) + ($($args_rest:tt)*) + + (#[method_id($($sel:tt)*)]) + () // No `optional` + ($($m_checked:tt)*) } => { - $crate::__method_msg_send_id! { - ($receiver) - ($($sel)*) - ($($args_rest)*) + $($m_checked)* + $($function_start)* { + #[allow(unused_unsafe)] + unsafe { + $crate::__method_msg_send_id! { + ($receiver) + ($($sel)*) + ($($args_rest)*) - () - () - () + () + () + () + } + } } }; // #[optional] { - @($receiver:expr) - @($($args_rest:tt)*) - @($($m_method:tt)*) - @($($m_optional:tt)*) - } => { - compile_error!("`#[optional]` is only supported in `extern_protocol!`") - }; -} + ($($function_start:tt)*) -#[doc(hidden)] -#[macro_export] -macro_rules! __extern_methods_get_receiver { - { - @(instance_method) - @( - $self_or_this:ident: $self_or_this_ty:ty, - _: $sel_ty:ty, - ) - } => { - $self_or_this - }; + ($__builder_method:ident) + ($receiver:expr) + ($($__args_prefix:tt)*) + ($($args_rest:tt)*) - { - @(class_method) - @( - _: $cls_ty:ty, - _: $sel_ty:ty, - ) + ($($m_method:tt)*) + ($($m_optional:tt)*) + ($($m_checked:tt)*) } => { - ::class() + $($m_checked)* + $($function_start)* { + compile_error!("`#[optional]` is only supported in `extern_protocol!`") + } }; } diff --git a/crates/objc2/src/macros/extern_protocol.rs b/crates/objc2/src/macros/extern_protocol.rs index dc67874fd..8418ec854 100644 --- a/crates/objc2/src/macros/extern_protocol.rs +++ b/crates/objc2/src/macros/extern_protocol.rs @@ -272,14 +272,14 @@ macro_rules! __extern_protocol_rewrite_methods { $($rest:tt)* } => { $crate::__rewrite_self_arg! { - ($crate::__extern_protocol_method_out) ($($args)*) - @($(#[$($m)*])*) - @($v unsafe fn $name($($args)*) $(-> $ret)?) - // Macro will add: - // @(kind) - // @(args_start) - // @(args_rest) + + ($crate::__extract_custom_attributes) + ($(#[$($m)*])*) + ($name) + + ($crate::__extern_protocol_method_out) + ($v unsafe fn $name($($args)*) $(-> $ret)?) } $crate::__extern_protocol_rewrite_methods! { @@ -295,14 +295,14 @@ macro_rules! __extern_protocol_rewrite_methods { $($rest:tt)* } => { $crate::__rewrite_self_arg! { - ($crate::__extern_protocol_method_out) ($($args)*) - @($(#[$($m)*])*) - @($v fn $name($($args)*) $(-> $ret)?) - // Macro will add: - // @(kind) - // @(args_start) - // @(args_rest) + + ($crate::__extract_custom_attributes) + ($(#[$($m)*])*) + ($name) + + ($crate::__extern_protocol_method_out) + ($v fn $name($($args)*) $(-> $ret)?) } $crate::__extern_protocol_rewrite_methods! { @@ -314,85 +314,81 @@ macro_rules! __extern_protocol_rewrite_methods { #[doc(hidden)] #[macro_export] macro_rules! __extern_protocol_method_out { + // #[method(...)] { - @($(#[$($m:tt)*])*) - @($($function_start:tt)*) - @(instance_method) - @( - $self_or_this:ident: $self_or_this_ty:ty, - _: $sel_ty:ty, - ) - @($($args_rest:tt)*) + ($($function_start:tt)*) + + (add_method) + ($receiver:expr) + ($($__args_prefix:tt)*) + ($($args_rest:tt)*) + + (#[method($($sel:tt)*)]) + ($($m_optional:tt)*) + ($($m_checked:tt)*) } => { - $crate::__strip_custom_attributes! { - @($(#[$($m)*])*) - @($($function_start)* { - #[allow(unused_unsafe)] - unsafe { - $crate::__extract_custom_attributes! { - @($(#[$($m)*])*) - @($crate::__extern_protocol_method_body) - @( - @($self_or_this) - @($($args_rest)*) - // Macro will add: - // @(method attribute) - // @(optional attribute) - ) - @() - @() - } + $($m_checked)* + $($function_start)* { + #[allow(unused_unsafe)] + unsafe { + $crate::__method_msg_send! { + ($receiver) + ($($sel)*) + ($($args_rest)*) + + () + () } - }) - @() + } } }; - { - @($(#[$($m:tt)*])*) - @($($function_start:tt)*) - @(class_method) - @($($args_start:tt)*) - @($($args_rest:tt)*) - } => { - compile_error!("class methods are not supported in `extern_protocol!`"); - }; -} -#[doc(hidden)] -#[macro_export] -macro_rules! __extern_protocol_method_body { - // #[method(...)] + // #[method_id(...)] { - @($receiver:expr) - @($($args_rest:tt)*) - @(#[method($($sel:tt)*)]) - @($($m_optional:tt)*) - } => { - $crate::__method_msg_send! { - ($receiver) - ($($sel)*) - ($($args_rest)*) + ($($function_start:tt)*) - () - () + (add_method) + ($receiver:expr) + ($($__args_prefix:tt)*) + ($($args_rest:tt)*) + + (#[method_id($($sel:tt)*)]) + ($($m_optional:tt)*) + ($($m_checked:tt)*) + } => { + $($m_checked)* + $($function_start)* { + #[allow(unused_unsafe)] + unsafe { + $crate::__method_msg_send_id! { + ($receiver) + ($($sel)*) + ($($args_rest)*) + + () + () + () + } + } } }; - // #[method_id(...)] + // Class method { - @($receiver:expr) - @($($args_rest:tt)*) - @(#[method_id($($sel:tt)*)]) - @($($m_optional:tt)*) + ($($function_start:tt)*) + + (add_class_method) + ($receiver:expr) + ($($__args_prefix:tt)*) + ($($args_rest:tt)*) + + ($($m_method:tt)*) + ($($m_optional:tt)*) + ($($m_checked:tt)*) } => { - $crate::__method_msg_send_id! { - ($receiver) - ($($sel)*) - ($($args_rest)*) - - () - () - () + $($m_checked)* + $($function_start)* { + compile_error!("class methods are not supported in `extern_protocol!`") } }; } diff --git a/crates/test-ui/ui/declare_class_invalid_syntax.stderr b/crates/test-ui/ui/declare_class_invalid_syntax.stderr index 2b144279a..b5f1bdc89 100644 --- a/crates/test-ui/ui/declare_class_invalid_syntax.stderr +++ b/crates/test-ui/ui/declare_class_invalid_syntax.stderr @@ -10,7 +10,7 @@ error: must specify the desired selector using `#[method(...)]` or `#[method_id( | | ); | |_^ | - = note: this error originates in the macro `$crate::__extract_custom_attributes` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::__extract_custom_attributes_inner` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) error: `#[method_id(...)]` is not supported in `declare_class!` yet --> ui/declare_class_invalid_syntax.rs @@ -76,6 +76,20 @@ note: while trying to match meta-variable `$body:block` | fn $name:ident($($args:tt)*) $(-> $ret:ty)? $body:block | ^^^^^^^^^^^ +error: must specify the desired selector using `#[method(...)]` or `#[method_id(...)]` + --> ui/declare_class_invalid_syntax.rs + | + | / declare_class!( + | | struct CustomObject; + | | + | | unsafe impl ClassType for CustomObject { +... | + | | } + | | ); + | |_^ + | + = note: this error originates in the macro `$crate::__extract_custom_attributes_inner` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) + error: no rules expected the token `(` --> ui/declare_class_invalid_syntax.rs | @@ -85,10 +99,10 @@ error: no rules expected the token `(` note: while trying to match `_` --> $WORKSPACE/crates/objc2/src/macros/declare_class.rs | - | (_ : $param_ty:ty, $($rest:tt)*) + | (_ : $param_ty:ty $(, $($rest_args:tt)*)?) | ^ -error: no rules expected the token `,` +error: no rules expected the token `)` --> ui/declare_class_invalid_syntax.rs | | / declare_class!( @@ -103,7 +117,7 @@ error: no rules expected the token `,` note: while trying to match `:` --> $WORKSPACE/crates/objc2/src/macros/declare_class.rs | - | ($param:ident : $param_ty:ty, $($rest:tt)*) + | ($param:ident : $param_ty:ty $(, $($rest_args:tt)*)?) | ^ = note: this error originates in the macro `$crate::__declare_class_method_out` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/crates/test-ui/ui/declare_class_invalid_type.stderr b/crates/test-ui/ui/declare_class_invalid_type.stderr index caee52c78..9642cad3d 100644 --- a/crates/test-ui/ui/declare_class_invalid_type.stderr +++ b/crates/test-ui/ui/declare_class_invalid_type.stderr @@ -26,7 +26,7 @@ note: required by a bound in `EncodeConvert` | | pub trait EncodeConvert: convert_private::Sealed { | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `EncodeConvert` - = note: this error originates in the macro `$crate::__declare_class_method_out` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::__declare_class_method_out_inner` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the trait bound `Vec<()>: Encode` is not satisfied --> ui/declare_class_invalid_type.rs @@ -56,7 +56,7 @@ note: required by a bound in `EncodeConvert` | | pub trait EncodeConvert: convert_private::Sealed { | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `EncodeConvert` - = note: this error originates in the macro `$crate::__declare_class_method_out` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::__declare_class_method_out_inner` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the trait bound `Box: Encode` is not satisfied --> ui/declare_class_invalid_type.rs @@ -86,7 +86,7 @@ note: required by a bound in `EncodeConvert` | | pub trait EncodeConvert: convert_private::Sealed { | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `EncodeConvert` - = note: this error originates in the macro `$crate::__fn_args` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::__declare_class_rewrite_args` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the trait bound `CustomObject: Encode` is not satisfied --> ui/declare_class_invalid_type.rs @@ -116,4 +116,4 @@ note: required by a bound in `EncodeConvert` | | pub trait EncodeConvert: convert_private::Sealed { | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `EncodeConvert` - = note: this error originates in the macro `$crate::__fn_args` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::__declare_class_rewrite_args` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/crates/test-ui/ui/extern_methods_invalid_type.stderr b/crates/test-ui/ui/extern_methods_invalid_type.stderr index f1ac8e2fa..705b330ff 100644 --- a/crates/test-ui/ui/extern_methods_invalid_type.stderr +++ b/crates/test-ui/ui/extern_methods_invalid_type.stderr @@ -71,7 +71,7 @@ note: associated function defined here | | unsafe fn send_message_id>( | ^^^^^^^^^^^^^^^ - = note: this error originates in the macro `$crate::__extern_methods_get_receiver` which comes from the expansion of the macro `extern_methods` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::__rewrite_self_arg_inner` 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 `Result<(), Id>: Encode` is not satisfied --> ui/extern_methods_invalid_type.rs diff --git a/crates/test-ui/ui/extern_methods_missing_method.stderr b/crates/test-ui/ui/extern_methods_missing_method.stderr index 974db8d3f..a4f29851d 100644 --- a/crates/test-ui/ui/extern_methods_missing_method.stderr +++ b/crates/test-ui/ui/extern_methods_missing_method.stderr @@ -8,4 +8,4 @@ error: must specify the desired selector using `#[method(...)]` or `#[method_id( | | ); | |_^ | - = note: this error originates in the macro `$crate::__extract_custom_attributes` which comes from the expansion of the macro `extern_methods` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::__extract_custom_attributes_inner` which comes from the expansion of the macro `extern_methods` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/crates/test-ui/ui/extern_methods_selector_twice.stderr b/crates/test-ui/ui/extern_methods_selector_twice.stderr index 248323509..0884abb4c 100644 --- a/crates/test-ui/ui/extern_methods_selector_twice.stderr +++ b/crates/test-ui/ui/extern_methods_selector_twice.stderr @@ -10,7 +10,7 @@ error: cannot specify the `method`/`method_id` attribute twice | | ); | |_^ | - = note: this error originates in the macro `$crate::__extract_custom_attributes` which comes from the expansion of the macro `extern_methods` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::__extract_custom_attributes_inner` which comes from the expansion of the macro `extern_methods` (in Nightly builds, run with -Z macro-backtrace for more info) error: cannot specify the `method`/`method_id` attribute twice --> ui/extern_methods_selector_twice.rs @@ -24,7 +24,7 @@ error: cannot specify the `method`/`method_id` attribute twice | | ); | |_^ | - = note: this error originates in the macro `$crate::__extract_custom_attributes` which comes from the expansion of the macro `extern_methods` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::__extract_custom_attributes_inner` which comes from the expansion of the macro `extern_methods` (in Nightly builds, run with -Z macro-backtrace for more info) error: cannot specify the `method`/`method_id` attribute twice --> ui/extern_methods_selector_twice.rs @@ -38,7 +38,7 @@ error: cannot specify the `method`/`method_id` attribute twice | | ); | |_^ | - = note: this error originates in the macro `$crate::__extract_custom_attributes` which comes from the expansion of the macro `extern_methods` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::__extract_custom_attributes_inner` which comes from the expansion of the macro `extern_methods` (in Nightly builds, run with -Z macro-backtrace for more info) error: cannot specify the `method`/`method_id` attribute twice --> ui/extern_methods_selector_twice.rs @@ -52,4 +52,4 @@ error: cannot specify the `method`/`method_id` attribute twice | | ); | |_^ | - = note: this error originates in the macro `$crate::__extract_custom_attributes` which comes from the expansion of the macro `extern_methods` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::__extract_custom_attributes_inner` which comes from the expansion of the macro `extern_methods` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/crates/test-ui/ui/extern_protocol_class_method.rs b/crates/test-ui/ui/extern_protocol_class_method.rs index fbeeb76c9..cca83b633 100644 --- a/crates/test-ui/ui/extern_protocol_class_method.rs +++ b/crates/test-ui/ui/extern_protocol_class_method.rs @@ -1,5 +1,5 @@ use objc2::{extern_protocol, ProtocolType}; -// use objc2::rc::{Id, Owned}; +use objc2::rc::{Id, Owned}; extern_protocol!( pub struct MyProtocol; diff --git a/crates/test-ui/ui/wrong_optional.stderr b/crates/test-ui/ui/wrong_optional.stderr index 4c0e159b7..5ec4ab9ec 100644 --- a/crates/test-ui/ui/wrong_optional.stderr +++ b/crates/test-ui/ui/wrong_optional.stderr @@ -10,7 +10,7 @@ error: `#[optional]` is only supported in `extern_protocol!` | | ); | |_^ | - = note: this error originates in the macro `$crate::__extern_methods_unsafe_method_body` which comes from the expansion of the macro `extern_methods` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::__extern_methods_method_out` which comes from the expansion of the macro `extern_methods` (in Nightly builds, run with -Z macro-backtrace for more info) error: `#[optional]` is only supported in `extern_protocol!` --> ui/wrong_optional.rs @@ -24,7 +24,7 @@ error: `#[optional]` is only supported in `extern_protocol!` | | ); | |_^ | - = note: this error originates in the macro `$crate::__extern_methods_unsafe_method_body` which comes from the expansion of the macro `extern_methods` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::__extern_methods_method_out` which comes from the expansion of the macro `extern_methods` (in Nightly builds, run with -Z macro-backtrace for more info) error: `#[optional]` is only supported in `extern_protocol!` --> ui/wrong_optional.rs From e5e22d737c62e47efd82400ed5fe88cdad0d23dd Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Mon, 16 Jan 2023 12:42:37 +0100 Subject: [PATCH 6/6] Allow #[method_id(...)] to be used in declare_class! --- crates/objc2/CHANGELOG.md | 2 + crates/objc2/src/__macro_helpers.rs | 11 + .../src/__macro_helpers/declare_class.rs | 113 ++++++++++ crates/objc2/src/declare.rs | 61 +++++- .../objc2/src/declare/declare_class_tests.rs | 28 ++- crates/objc2/src/macros.rs | 9 + crates/objc2/src/macros/__rewrite_self_arg.rs | 14 +- crates/objc2/src/macros/declare_class.rs | 153 +++++++++++-- crates/objc2/src/macros/extern_methods.rs | 3 + crates/objc2/src/macros/extern_protocol.rs | 3 + crates/objc2/src/rc/id.rs | 27 ++- crates/objc2/src/rc/test_object.rs | 123 +++++++---- crates/objc2/tests/declare_class_self.rs | 52 +++++ crates/objc2/tests/no_prelude.rs | 5 + crates/test-ui/ui/declare_add_bad_method.rs | 38 ++++ .../test-ui/ui/declare_add_bad_method.stderr | 119 ++++++++++ .../ui/declare_class_invalid_receiver.rs | 31 ++- .../ui/declare_class_invalid_receiver.stderr | 204 ++++++++++++++++++ .../ui/declare_class_invalid_syntax.rs | 30 ++- .../ui/declare_class_invalid_syntax.stderr | 117 ++++++++-- .../test-ui/ui/declare_class_invalid_type2.rs | 25 +++ .../ui/declare_class_invalid_type2.stderr | 34 +++ crates/test-ui/ui/wrong_optional.rs | 20 +- crates/test-ui/ui/wrong_optional.stderr | 10 +- 24 files changed, 1120 insertions(+), 112 deletions(-) create mode 100644 crates/objc2/src/__macro_helpers/declare_class.rs create mode 100644 crates/objc2/tests/declare_class_self.rs create mode 100644 crates/test-ui/ui/declare_add_bad_method.rs create mode 100644 crates/test-ui/ui/declare_add_bad_method.stderr create mode 100644 crates/test-ui/ui/declare_class_invalid_type2.rs create mode 100644 crates/test-ui/ui/declare_class_invalid_type2.stderr diff --git a/crates/objc2/CHANGELOG.md b/crates/objc2/CHANGELOG.md index 902f57d62..d66b8bc1d 100644 --- a/crates/objc2/CHANGELOG.md +++ b/crates/objc2/CHANGELOG.md @@ -10,6 +10,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). * Support `#[cfg(...)]` attributes in `extern_class!` macro. * Added support for selectors with multiple colons like `abc::` in the `sel!`, `extern_class!`, `extern_protocol!` and `declare_class!` macros. +* Added ability to use `#[method_id(mySelector:)]` inside `declare_class!`, + just like you would do in `extern_methods!`. ### Changed * **BREAKING**: Using the automatic `NSError**`-to-`Result` functionality in diff --git a/crates/objc2/src/__macro_helpers.rs b/crates/objc2/src/__macro_helpers.rs index aa1e07e6d..83e8b675a 100644 --- a/crates/objc2/src/__macro_helpers.rs +++ b/crates/objc2/src/__macro_helpers.rs @@ -30,6 +30,10 @@ pub use core::{compile_error, concat, panic, stringify}; // TODO: Use `core::cell::LazyCell` pub use std::sync::Once; +mod declare_class; + +pub use self::declare_class::{MaybeOptionId, MessageRecieveId}; + // Common selectors. // // These are put here to deduplicate the cached selector, and when using @@ -850,6 +854,13 @@ mod tests { let _obj: Id<__RcTestObject, Shared> = unsafe { msg_send_id![&obj, methodReturningNull] }; } + #[test] + #[should_panic = "unexpected NULL returned from -[__RcTestObject aMethod:]"] + fn test_normal_with_param_and_null() { + let obj = Id::into_shared(__RcTestObject::new()); + let _obj: Id<__RcTestObject, Shared> = unsafe { msg_send_id![&obj, aMethod: false] }; + } + #[test] #[should_panic = "unexpected NULL description; receiver was NULL"] #[cfg(not(debug_assertions))] // Does NULL receiver checks diff --git a/crates/objc2/src/__macro_helpers/declare_class.rs b/crates/objc2/src/__macro_helpers/declare_class.rs new file mode 100644 index 000000000..016193053 --- /dev/null +++ b/crates/objc2/src/__macro_helpers/declare_class.rs @@ -0,0 +1,113 @@ +use core::mem::ManuallyDrop; +use core::ptr; + +use crate::declare::__IdReturnValue; +use crate::rc::{Allocated, Id, Ownership}; +use crate::{Message, MessageReceiver}; + +use super::{CopyOrMutCopy, Init, MaybeUnwrap, New, Other}; + +// One could imagine a different design where we simply had a method like +// `fn convert_receiver()`, but that won't work in `declare_class!` since we +// can't actually modify the `self` argument (e.g. `let self = foo(self)` is +// not allowed). +// +// See `MsgSendId` and `RetainSemantics` for details on the retain semantics +// we're following here. +pub trait MessageRecieveId { + fn into_return(obj: Ret) -> __IdReturnValue; +} + +// Receiver and return type have no correlation +impl MessageRecieveId for New +where + Receiver: MessageReceiver, + Ret: MaybeOptionId, +{ + #[inline] + fn into_return(obj: Ret) -> __IdReturnValue { + obj.consumed_return() + } +} + +// Explicitly left unimplemented for now! +// impl MessageRecieveId>> for Alloc {} + +// Note: `MethodImplementation` allows for `Allocated` as the receiver, so we +// restrict it here to only be when the selector is `init`. +// +// Additionally, the receiver and return type must have the same generic +// generic parameter `T`. +impl MessageRecieveId, Ret> for Init +where + T: Message, + O: Ownership, + Ret: MaybeOptionId>, +{ + #[inline] + fn into_return(obj: Ret) -> __IdReturnValue { + obj.consumed_return() + } +} + +// Receiver and return type have no correlation +impl MessageRecieveId for CopyOrMutCopy +where + Receiver: MessageReceiver, + Ret: MaybeOptionId, +{ + #[inline] + fn into_return(obj: Ret) -> __IdReturnValue { + obj.consumed_return() + } +} + +// Receiver and return type have no correlation +impl MessageRecieveId for Other +where + Receiver: MessageReceiver, + Ret: MaybeOptionId, +{ + #[inline] + fn into_return(obj: Ret) -> __IdReturnValue { + obj.autorelease_return() + } +} + +/// Helper trait for specifying an `Id` or an `Option>`. +/// +/// (Both of those are valid return types from declare_class! `#[method_id]`). +pub trait MaybeOptionId: MaybeUnwrap { + fn consumed_return(self) -> __IdReturnValue; + fn autorelease_return(self) -> __IdReturnValue; +} + +impl MaybeOptionId for Id { + #[inline] + fn consumed_return(self) -> __IdReturnValue { + let ptr: *mut T = Id::consume_as_ptr(ManuallyDrop::new(self)); + __IdReturnValue(ptr.cast()) + } + + #[inline] + fn autorelease_return(self) -> __IdReturnValue { + let ptr: *mut T = Id::autorelease_return(self); + __IdReturnValue(ptr.cast()) + } +} + +impl MaybeOptionId for Option> { + #[inline] + fn consumed_return(self) -> __IdReturnValue { + let ptr: *mut T = self + .map(|this| Id::consume_as_ptr(ManuallyDrop::new(this))) + .unwrap_or_else(ptr::null_mut); + __IdReturnValue(ptr.cast()) + } + + #[inline] + fn autorelease_return(self) -> __IdReturnValue { + let ptr: *mut T = Id::autorelease_return_option(self); + __IdReturnValue(ptr.cast()) + } +} diff --git a/crates/objc2/src/declare.rs b/crates/objc2/src/declare.rs index 3941e8f43..1f1632d03 100644 --- a/crates/objc2/src/declare.rs +++ b/crates/objc2/src/declare.rs @@ -124,6 +124,7 @@ use std::ffi::CString; use crate::encode::{Encode, EncodeArguments, Encoding, RefEncode}; use crate::ffi; +use crate::rc::Allocated; use crate::runtime::{Bool, Class, Imp, Object, Protocol, Sel}; use crate::sel; use crate::Message; @@ -196,6 +197,37 @@ macro_rules! method_decl_impl { } } }; + (@<> Allocated, $f:ty, $($t:ident),*) => { + #[doc(hidden)] + impl private::Sealed for $f + where + T: Message + ?Sized, + $($t: Encode,)* + {} + + #[doc(hidden)] + impl MethodImplementation for $f + where + T: Message + ?Sized, + $($t: Encode,)* + { + type Callee = T; + type Ret = __IdReturnValue; + type Args = ($($t,)*); + + fn __imp(self) -> Imp { + // SAFETY: `Allocated` is the same as `NonNull`, except + // with the assumption of a +1 calling convention. + // + // The calling convention is ensured to be upheld by having + // `__IdReturnValue` in the type, since that type is private + // and hence only internal macros like `#[method_id]` will be + // able to produce it (and that, in turn, only allows it if + // the selector is `init` as checked by `MessageRecieveId`). + unsafe { mem::transmute(self) } + } + } + }; (# $abi:literal; $($t:ident),*) => { method_decl_impl!(@<'a> T, R, extern $abi fn(&'a T, Sel $(, $t)*) -> R, $($t),*); method_decl_impl!(@<'a> T, R, extern $abi fn(&'a mut T, Sel $(, $t)*) -> R, $($t),*); @@ -207,6 +239,9 @@ macro_rules! method_decl_impl { method_decl_impl!(@<'a> Class, R, extern $abi fn(&'a Class, Sel $(, $t)*) -> R, $($t),*); method_decl_impl!(@<> Class, R, unsafe extern $abi fn(*const Class, Sel $(, $t)*) -> R, $($t),*); method_decl_impl!(@<'a> Class, R, unsafe extern $abi fn(&'a Class, Sel $(, $t)*) -> R, $($t),*); + + method_decl_impl!(@<> Allocated, extern $abi fn(Allocated, Sel $(, $t)*) -> __IdReturnValue, $($t),*); + method_decl_impl!(@<> Allocated, unsafe extern $abi fn(Allocated, Sel $(, $t)*) -> __IdReturnValue, $($t),*); }; ($($t:ident),*) => { method_decl_impl!(# "C"; $($t),*); @@ -229,6 +264,17 @@ method_decl_impl!(A, B, C, D, E, F, G, H, I, J); method_decl_impl!(A, B, C, D, E, F, G, H, I, J, K); method_decl_impl!(A, B, C, D, E, F, G, H, I, J, K, L); +/// Helper type for implementing `MethodImplementation` with a receiver of +/// `Allocated`, without exposing that implementation to users. +#[doc(hidden)] +#[repr(transparent)] +pub struct __IdReturnValue(pub(crate) *mut Object); + +// SAFETY: `__IdReturnValue` is `#[repr(transparent)]` +unsafe impl Encode for __IdReturnValue { + const ENCODING: Encoding = <*mut Object>::ENCODING; +} + fn method_type_encoding(ret: &Encoding, args: &[Encoding]) -> CString { // First two arguments are always self and the selector let mut types = format!("{ret}{}{}", <*mut Object>::ENCODING, Sel::ENCODING); @@ -630,6 +676,7 @@ impl ProtocolBuilder { #[cfg(test)] mod tests { use super::*; + use crate::rc::{Id, Shared}; use crate::runtime::{NSObject, NSZone}; use crate::test_utils; use crate::{declare_class, extern_protocol, msg_send, ClassType, ConformsTo, ProtocolType}; @@ -641,8 +688,8 @@ mod tests { const NAME: &'static str = "NSCopying"; #[allow(unused)] - #[method(copyWithZone:)] - fn copy_with_zone(&self, _zone: *const NSZone) -> *mut Self; + #[method_id(copyWithZone:)] + fn copy_with_zone(&self, _zone: *const NSZone) -> Id; } ); @@ -814,9 +861,8 @@ mod tests { } unsafe impl ConformsTo for Custom { - #[method(copyWithZone:)] - #[allow(unreachable_code)] - fn copy_with_zone(&self, _zone: *const NSZone) -> *mut Self { + #[method_id(copyWithZone:)] + fn copy_with_zone(&self, _zone: *const NSZone) -> Id { unimplemented!() } } @@ -910,9 +956,8 @@ mod tests { } unsafe impl ConformsTo for Custom { - #[method(copyWithZone:)] - #[allow(unreachable_code)] - fn copy_with_zone(&self, _zone: *const NSZone) -> *mut Self { + #[method_id(copyWithZone:)] + fn copy_with_zone(&self, _zone: *const NSZone) -> Id { unimplemented!() } diff --git a/crates/objc2/src/declare/declare_class_tests.rs b/crates/objc2/src/declare/declare_class_tests.rs index 6e6330322..1558d61c9 100644 --- a/crates/objc2/src/declare/declare_class_tests.rs +++ b/crates/objc2/src/declare/declare_class_tests.rs @@ -1,4 +1,4 @@ -#![deny(deprecated)] +#![deny(deprecated, unreachable_code)] use core::ptr; use crate::rc::{Id, Owned, Shared}; @@ -199,15 +199,15 @@ declare_class!( true } - #[method(test:::withObject:)] + #[method_id(test:::withObject:)] fn _test_object( &self, _arg1: i32, _arg2: i32, _arg3: i32, _obj: *const Self, - ) -> *const Self { - ptr::null() + ) -> Option> { + None } } ); @@ -290,6 +290,16 @@ declare_class!( fn takes_returns_bool_instance(&self, b: bool) -> bool { b } + + #[method_id(idTakesBool:)] + fn id_takes_bool(_b: bool) -> Option> { + None + } + + #[method_id(idTakesBoolInstance:)] + fn id_takes_bool_instance(&self, _b: bool) -> Option> { + None + } } ); @@ -326,6 +336,16 @@ declare_class!( fn unreachable_class_void() { unreachable!() } + + #[method_id(unreachableId)] + fn unreachable_id(&self) -> Id { + unreachable!() + } + + #[method_id(unreachableClassId)] + fn unreachable_class_id() -> Id { + unreachable!() + } } ); diff --git a/crates/objc2/src/macros.rs b/crates/objc2/src/macros.rs index 7561212dc..4fe74d285 100644 --- a/crates/objc2/src/macros.rs +++ b/crates/objc2/src/macros.rs @@ -245,6 +245,15 @@ macro_rules! __sel_helper { } => ({ $crate::__sel_data!($($parsed_sel)*) }); + // Single identifier + { + @() + $ident:ident + } => { + $crate::__sel_helper! { + @($ident) + } + }; // Parse identitifer + colon token { @($($parsed_sel:tt)*) diff --git a/crates/objc2/src/macros/__rewrite_self_arg.rs b/crates/objc2/src/macros/__rewrite_self_arg.rs index 5d34bba97..5df592962 100644 --- a/crates/objc2/src/macros/__rewrite_self_arg.rs +++ b/crates/objc2/src/macros/__rewrite_self_arg.rs @@ -2,8 +2,9 @@ /// /// Will add: /// ```ignore -/// (builder method) -/// (receiver) +/// (builder_method:ident) +/// (receiver:expr) +/// (receiver_ty:ty) /// (args_prefix*) /// (args_rest*) /// ``` @@ -44,6 +45,7 @@ macro_rules! __rewrite_self_arg_inner { (add_method) ($self) + (&Self) ( &$self, _: $crate::runtime::Sel, @@ -63,6 +65,7 @@ macro_rules! __rewrite_self_arg_inner { (add_method) ($self) + (&mut Self) ( &mut $self, _: $crate::runtime::Sel, @@ -82,6 +85,7 @@ macro_rules! __rewrite_self_arg_inner { (add_method) ($self) + ($self_ty) ( $self: $self_ty, _: $crate::runtime::Sel, @@ -101,6 +105,7 @@ macro_rules! __rewrite_self_arg_inner { (add_method) ($self) + ($self_ty) ( $mut $self: $self_ty, _: $crate::runtime::Sel, @@ -124,6 +129,7 @@ macro_rules! __rewrite_self_arg_inner { (add_method) ($this) + ($this_ty) ( $mut $this: $this_ty, _: $crate::runtime::Sel, @@ -143,6 +149,7 @@ macro_rules! __rewrite_self_arg_inner { (add_method) ($this) + ($this_ty) ( $this: $this_ty, _: $crate::runtime::Sel, @@ -162,6 +169,7 @@ macro_rules! __rewrite_self_arg_inner { (add_method) ($this) + ($this_ty) ( $mut $this: $this_ty, _: $crate::runtime::Sel, @@ -181,6 +189,7 @@ macro_rules! __rewrite_self_arg_inner { (add_method) ($this) + ($this_ty) ( $this: $this_ty, _: $crate::runtime::Sel, @@ -202,6 +211,7 @@ macro_rules! __rewrite_self_arg_inner { (add_class_method) (::class()) + (&$crate::runtime::Class) ( _: &$crate::runtime::Class, _: $crate::runtime::Sel, diff --git a/crates/objc2/src/macros/declare_class.rs b/crates/objc2/src/macros/declare_class.rs index 11aeab07d..97e195059 100644 --- a/crates/objc2/src/macros/declare_class.rs +++ b/crates/objc2/src/macros/declare_class.rs @@ -49,8 +49,12 @@ /// will be registered as a class method. /// /// The desired selector can be specified using the `#[method(my:selector:)]` -/// attribute, similar to the [`extern_methods!`] macro (`method_id` is not -/// yet supported, see [#282]). +/// or `#[method_id(my:selector:)]` attribute, similar to the +/// [`extern_methods!`] macro. +/// +/// If the `#[method_id(...)]` attribute is used, the return type must be +/// `Option>` or `Id`. Additionally, if the selector is in the +/// "init"-family, the "self"/"this" argument must be `Allocated`. /// /// Putting other attributes on the method such as `cfg`, `allow`, `doc`, /// `deprecated` and so on is supported. However, note that `cfg_attr` may not @@ -69,7 +73,6 @@ /// ["associated functions"]: https://doc.rust-lang.org/reference/items/associated-items.html#methods /// ["methods"]: https://doc.rust-lang.org/reference/items/associated-items.html#methods /// [`extern_methods!`]: crate::extern_methods -/// [#282]: https://github.com/madsmtm/objc2/issues/282 /// [open an issue]: https://github.com/madsmtm/objc2/issues/new /// [`msg_send!`]: crate::msg_send /// [`runtime::Bool`]: crate::runtime::Bool @@ -198,9 +201,9 @@ /// *self.foo /// } /// -/// #[method(object)] -/// fn __get_object(&self) -> *mut NSObject { -/// Id::autorelease_return(self.object.clone()) +/// #[method_id(object)] +/// fn __get_object(&self) -> Id { +/// self.object.clone() /// } /// /// #[method(myClassMethod)] @@ -731,6 +734,7 @@ macro_rules! __declare_class_method_out { ($builder_method:ident) ($receiver:expr) + ($receiver_ty:ty) ($($args_prefix:tt)*) ($($args_rest:tt)*) @@ -752,6 +756,7 @@ macro_rules! __declare_class_method_out { ($builder_method) ($receiver) + ($receiver_ty) ($($args_prefix)*) ($($m_method)*) @@ -845,17 +850,19 @@ macro_rules! __declare_class_rewrite_args { #[doc(hidden)] #[macro_export] macro_rules! __declare_class_method_out_inner { + // #[method(...)] { ($($qualifiers:tt)*) ($name:ident) - () + ($($ret:ty)?) ($body:block) ($__builder_method:ident) ($__receiver:expr) + ($__receiver_ty:ty) ($($args_prefix:tt)*) - ($($__m_method:tt)*) + (#[method($($__sel:tt)*)]) ($($__m_optional:tt)*) ($($m_checked:tt)*) @@ -866,11 +873,15 @@ macro_rules! __declare_class_method_out_inner { $($qualifiers)* extern "C" fn $name( $($args_prefix)* $($args_converted)* - ) { + ) $(-> <$ret as $crate::encode::EncodeConvert>::__Inner)? { $($body_prefix)* - $body + $crate::__convert_result! { + $body $(; $ret)? + } } }; + + // #[method_id(...)] { ($($qualifiers:tt)*) ($name:ident) @@ -879,9 +890,10 @@ macro_rules! __declare_class_method_out_inner { ($__builder_method:ident) ($__receiver:expr) + ($receiver_ty:ty) ($($args_prefix:tt)*) - ($($__m_method:tt)*) + (#[method_id($($sel:tt)*)]) ($($__m_optional:tt)*) ($($m_checked:tt)*) @@ -892,18 +904,68 @@ macro_rules! __declare_class_method_out_inner { $($qualifiers)* extern "C" fn $name( $($args_prefix)* $($args_converted)* - ) -> <$ret as $crate::encode::EncodeConvert>::__Inner { + ) -> $crate::declare::__IdReturnValue { $($body_prefix)* + let __objc2_result = $body; + #[allow(unreachable_code)] - <$ret as $crate::encode::EncodeConvert>::__into_inner(__objc2_result) + <$crate::__macro_helpers::RetainSemantics<{ + $crate::__macro_helpers::retain_semantics( + $crate::__sel_helper! { + @() + $($sel)* + } + ) + }> as $crate::__macro_helpers::MessageRecieveId< + $receiver_ty, + $ret, + >>::into_return(__objc2_result) } }; + + { + ($($qualifiers:tt)*) + ($name:ident) + () + ($body:block) + + ($__builder_method:ident) + ($__receiver:expr) + ($__receiver_ty:ty) + ($($args_prefix:tt)*) + + (#[method_id($($sel:tt)*)]) + ($($__m_optional:tt)*) + ($($m_checked:tt)*) + + ($($args_converted:tt)*) + ($($body_prefix:tt)*) + } => { + $($m_checked)* + $($qualifiers)* extern "C" fn $name() { + compile_error!("`#[method_id(...)]` must have a return type") + } + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! __convert_result { + ($body:block) => { + $body + }; + ($body:block; $ret:ty) => { + let __objc2_result = $body; + #[allow(unreachable_code)] + <$ret as $crate::encode::EncodeConvert>::__into_inner(__objc2_result) + }; } #[doc(hidden)] #[macro_export] macro_rules! __declare_class_register_out { + // #[method(...)] { ($builder:ident) ($($qualifiers:tt)*) @@ -913,6 +975,7 @@ macro_rules! __declare_class_register_out { ($builder_method:ident) ($__receiver:expr) + ($__receiver_ty:ty) ($($__args_prefix:tt)*) ($($args_rest:tt)*) @@ -935,6 +998,7 @@ macro_rules! __declare_class_register_out { } }; + // #[method_id(...)] { ($builder:ident) ($($qualifiers:tt)*) @@ -944,16 +1008,30 @@ macro_rules! __declare_class_register_out { ($builder_method:ident) ($__receiver:expr) + ($__receiver_ty:ty) ($($__args_prefix:tt)*) ($($args_rest:tt)*) - (#[method($($sel:tt)*)]) - ($($m_optional:tt)*) + (#[method_id($($sel:tt)*)]) + () // No optional ($($m_checked:tt)*) } => { - compile_error!("`#[optional]` is only supported in `extern_protocol!`") + $crate::__extract_and_apply_cfg_attributes! { + @($($m_checked)*) + @( + $builder.$builder_method( + $crate::__get_method_id_sel!($($sel)*), + Self::$name as $crate::__fn_ptr! { + ($($qualifiers)*) + (_, _,) + $($args_rest)* + }, + ); + ) + } }; + // #[optional] { ($builder:ident) ($($qualifiers:tt)*) @@ -963,14 +1041,53 @@ macro_rules! __declare_class_register_out { ($builder_method:ident) ($__receiver:expr) + ($__receiver_ty:ty) ($($__args_prefix:tt)*) ($($args_rest:tt)*) - (#[method_id($($sel:tt)*)]) + ($($m_method:tt)*) ($($m_optional:tt)*) ($($m_checked:tt)*) } => { - compile_error!("`#[method_id(...)]` is not supported in `declare_class!` yet") + compile_error!("`#[optional]` is only supported in `extern_protocol!`") + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! __get_method_id_sel { + (alloc) => { + compile_error!(concat!( + "`#[method_id(alloc)]` is not supported. ", + "Use `#[method(alloc)]` and do the memory management yourself", + )) + }; + (retain) => { + compile_error!(concat!( + "`#[method_id(retain)]` is not supported. ", + "Use `#[method(retain)]` and do the memory management yourself", + )) + }; + (release) => { + compile_error!(concat!( + "`#[method_id(release)]` is not supported. ", + "Use `#[method(release)]` and do the memory management yourself", + )) + }; + (autorelease) => { + compile_error!(concat!( + "`#[method_id(autorelease)]` is not supported. ", + "Use `#[method(autorelease)]` and do the memory management yourself", + )) + }; + (dealloc) => { + compile_error!(concat!( + "`#[method_id(dealloc)]` is not supported. ", + "Add an instance variable with a `Drop` impl to the class instead", + )) + }; + ($($t:tt)*) => { + $crate::sel!($($t)*) }; } diff --git a/crates/objc2/src/macros/extern_methods.rs b/crates/objc2/src/macros/extern_methods.rs index dc0e39bd7..f0fb6e593 100644 --- a/crates/objc2/src/macros/extern_methods.rs +++ b/crates/objc2/src/macros/extern_methods.rs @@ -280,6 +280,7 @@ macro_rules! __extern_methods_method_out { ($__builder_method:ident) ($receiver:expr) + ($__receiver_ty:ty) ($($__args_prefix:tt)*) ($($args_rest:tt)*) @@ -309,6 +310,7 @@ macro_rules! __extern_methods_method_out { ($__builder_method:ident) ($receiver:expr) + ($__receiver_ty:ty) ($($__args_prefix:tt)*) ($($args_rest:tt)*) @@ -339,6 +341,7 @@ macro_rules! __extern_methods_method_out { ($__builder_method:ident) ($receiver:expr) + ($__receiver_ty:ty) ($($__args_prefix:tt)*) ($($args_rest:tt)*) diff --git a/crates/objc2/src/macros/extern_protocol.rs b/crates/objc2/src/macros/extern_protocol.rs index 8418ec854..6f84fa693 100644 --- a/crates/objc2/src/macros/extern_protocol.rs +++ b/crates/objc2/src/macros/extern_protocol.rs @@ -320,6 +320,7 @@ macro_rules! __extern_protocol_method_out { (add_method) ($receiver:expr) + ($__receiver_ty:ty) ($($__args_prefix:tt)*) ($($args_rest:tt)*) @@ -349,6 +350,7 @@ macro_rules! __extern_protocol_method_out { (add_method) ($receiver:expr) + ($__receiver_ty:ty) ($($__args_prefix:tt)*) ($($args_rest:tt)*) @@ -379,6 +381,7 @@ macro_rules! __extern_protocol_method_out { (add_class_method) ($receiver:expr) + ($__receiver_ty:ty) ($($__args_prefix:tt)*) ($($args_rest:tt)*) diff --git a/crates/objc2/src/rc/id.rs b/crates/objc2/src/rc/id.rs index dfa3c78c7..8e309b3b1 100644 --- a/crates/objc2/src/rc/id.rs +++ b/crates/objc2/src/rc/id.rs @@ -3,7 +3,7 @@ use core::marker::PhantomData; use core::mem::ManuallyDrop; use core::ops::{Deref, DerefMut}; use core::panic::{RefUnwindSafe, UnwindSafe}; -use core::ptr::NonNull; +use core::ptr::{self, NonNull}; use super::AutoreleasePool; use super::{Owned, Ownership, Shared}; @@ -456,6 +456,21 @@ impl Id { res } + #[inline] + pub(crate) fn autorelease_return_option(this: Option) -> *mut T { + let ptr: *mut T = this + .map(|this| ManuallyDrop::new(this).ptr.as_ptr()) + .unwrap_or_else(ptr::null_mut); + + // SAFETY: Same as `autorelease_inner`, this is just an optimization. + let res: *mut T = unsafe { ffi::objc_autoreleaseReturnValue(ptr.cast()) }.cast(); + debug_assert_eq!( + res, ptr, + "objc_autoreleaseReturnValue did not return the same pointer" + ); + res + } + /// Autoreleases and prepares the [`Id`] to be returned to Objective-C. /// /// The object is not immediately released, but will be when the innermost @@ -510,15 +525,7 @@ impl Id { #[inline] pub fn autorelease_return(self) -> *mut T { // See `autorelease_inner` for why this is an inherent method - - let ptr = ManuallyDrop::new(self).ptr.as_ptr(); - // SAFETY: Same as `autorelease_inner`, this is just an optimization. - let res: *mut T = unsafe { ffi::objc_autoreleaseReturnValue(ptr.cast()) }.cast(); - debug_assert_eq!( - res, ptr, - "objc_autoreleaseReturnValue did not return the same pointer" - ); - res + Self::autorelease_return_option(Some(self)) } } diff --git a/crates/objc2/src/rc/test_object.rs b/crates/objc2/src/rc/test_object.rs index 750355017..0b83e10d6 100644 --- a/crates/objc2/src/rc/test_object.rs +++ b/crates/objc2/src/rc/test_object.rs @@ -1,8 +1,7 @@ use core::cell::RefCell; -use core::mem::ManuallyDrop; use core::ptr; -use super::{Id, Owned}; +use super::{Allocated, Id, Owned}; use crate::runtime::{NSObject, NSZone}; use crate::{declare_class, msg_send, msg_send_id, ClassType}; @@ -61,19 +60,19 @@ declare_class!( } unsafe impl __RcTestObject { - #[method(newReturningNull)] - fn new_returning_null() -> *mut Self { - ptr::null_mut() + #[method_id(newReturningNull)] + fn new_returning_null() -> Option> { + None } - #[method(newMethodOnInstance)] - fn new_method_on_instance(&self) -> *mut Self { - Id::consume_as_ptr(ManuallyDrop::new(Self::new())) + #[method_id(newMethodOnInstance)] + fn new_method_on_instance(&self) -> Id { + Self::new() } - #[method(newMethodOnInstanceNull)] - fn new_method_on_instance_null(&self) -> *mut Self { - ptr::null_mut() + #[method_id(newMethodOnInstanceNull)] + fn new_method_on_instance_null(&self) -> Option> { + None } #[method(alloc)] @@ -102,10 +101,9 @@ declare_class!( unsafe { msg_send![super(this), init] } } - #[method(initReturningNull)] - fn init_returning_null(&mut self) -> *mut Self { - let _: () = unsafe { msg_send![self, release] }; - ptr::null_mut() + #[method_id(initReturningNull)] + fn init_returning_null(_this: Allocated) -> Option> { + None } #[method(retain)] @@ -143,26 +141,31 @@ declare_class!( res } - #[method(copyWithZone:)] - fn copy_with_zone(&self, _zone: *const NSZone) -> *const Self { + #[method_id(copyWithZone:)] + fn copy_with_zone(&self, _zone: *const NSZone) -> Id { TEST_DATA.with(|data| data.borrow_mut().copy += 1); - Id::consume_as_ptr(ManuallyDrop::new(Self::new())) + Self::new() } - #[method(mutableCopyWithZone:)] - fn mutable_copy_with_zone(&self, _zone: *const NSZone) -> *const Self { + #[method_id(mutableCopyWithZone:)] + fn mutable_copy_with_zone(&self, _zone: *const NSZone) -> Id { TEST_DATA.with(|data| data.borrow_mut().mutable_copy += 1); - Id::consume_as_ptr(ManuallyDrop::new(Self::new())) + Self::new() + } + + #[method_id(copyReturningNull)] + fn copy_returning_null(_this: &Self) -> Option> { + None } - #[method(copyReturningNull)] - fn copy_returning_null(_this: &Self) -> *const Self { - ptr::null() + #[method_id(methodReturningNull)] + fn method_returning_null(self: &Self) -> Option> { + None } - #[method(methodReturningNull)] - fn method_returning_null(self: &Self) -> *const Self { - ptr::null() + #[method_id(aMethod:)] + fn a_method(&self, param: bool) -> Option> { + param.then(Self::new) } #[method(boolAndShouldError:error:)] @@ -193,46 +196,49 @@ declare_class!( } } - #[method(idAndShouldError:error:)] + #[method_id(idAndShouldError:error:)] fn class_error_id( should_error: bool, err: Option<&mut *mut __RcTestObject>, - ) -> *mut __RcTestObject { + ) -> Option> { if should_error { if let Some(err) = err { *err = __RcTestObject::new().autorelease_inner(); } - ptr::null_mut() + None } else { - __RcTestObject::new().autorelease_return() + Some(Self::new()) } } - #[method(idAndShouldError:error:)] + #[method_id(idAndShouldError:error:)] fn instance_error_id( self: &Self, should_error: bool, err: Option<&mut *mut __RcTestObject>, - ) -> *mut __RcTestObject { + ) -> Option> { if should_error { if let Some(err) = err { *err = __RcTestObject::new().autorelease_inner(); } - ptr::null_mut() + None } else { - __RcTestObject::new().autorelease_return() + Some(Self::new()) } } - #[method(newAndShouldError:error:)] - fn new_error(should_error: bool, err: Option<&mut *mut __RcTestObject>) -> *mut Self { + #[method_id(newAndShouldError:error:)] + fn new_error( + should_error: bool, + err: Option<&mut *mut __RcTestObject>, + ) -> Option> { if should_error { if let Some(err) = err { *err = __RcTestObject::new().autorelease_inner(); } - ptr::null_mut() + None } else { - unsafe { msg_send![Self::class(), new] } + unsafe { msg_send_id![Self::class(), new] } } } @@ -248,20 +254,19 @@ declare_class!( } } - #[method(initAndShouldError:error:)] + #[method_id(initAndShouldError:error:)] fn init_error( - this: &mut Self, + this: Allocated, should_error: bool, err: Option<&mut *mut __RcTestObject>, - ) -> *mut Self { + ) -> Option> { if should_error { if let Some(err) = err { *err = __RcTestObject::new().autorelease_inner(); } - let _: () = unsafe { msg_send![this, release] }; - ptr::null_mut() + None } else { - unsafe { msg_send![this, init] } + unsafe { msg_send_id![Some(this), init] } } } } @@ -475,4 +480,32 @@ mod tests { expected.dealloc += 1; expected.assert_current(); } + + #[test] + fn test_method_id_with_param() { + let mut expected = __ThreadTestData::current(); + + let obj = __RcTestObject::new(); + expected.alloc += 1; + expected.init += 1; + expected.assert_current(); + + let res: Option> = unsafe { msg_send_id![&obj, aMethod: false] }; + assert!(res.is_none()); + expected.assert_current(); + + let _res = autoreleasepool(|_pool| { + let res: Option> = + unsafe { msg_send_id![&obj, aMethod: true] }; + assert!(res.is_some()); + expected.alloc += 1; + expected.init += 1; + expected.autorelease += IF_AUTORELEASE_NOT_SKIPPED; + expected.retain += IF_AUTORELEASE_NOT_SKIPPED; + expected.assert_current(); + res + }); + expected.release += IF_AUTORELEASE_NOT_SKIPPED; + expected.assert_current(); + } } diff --git a/crates/objc2/tests/declare_class_self.rs b/crates/objc2/tests/declare_class_self.rs new file mode 100644 index 000000000..055656d15 --- /dev/null +++ b/crates/objc2/tests/declare_class_self.rs @@ -0,0 +1,52 @@ +//! To remind myself that `Self` needs to work in methods in `declare_class!`, +//! and hence we _must_ implement things by changing the generated method, we +//! can't just create an internal helper function (since we can't name the +//! types of such a function)! +use objc2::rc::{Allocated, Id, Shared}; +use objc2::runtime::NSObject; +use objc2::{declare_class, ClassType}; + +trait GetSameType { + type SameType: ?Sized; +} + +impl GetSameType for T { + type SameType = T; +} + +macro_rules! get_self { + () => { + Self + }; +} + +declare_class!( + struct MyTestObject; + + unsafe impl ClassType for MyTestObject { + type Super = NSObject; + } + + unsafe impl MyTestObject { + #[method_id(init)] + fn init( + _this: Allocated<::SameType>, + _param: <*const Self as GetSameType>::SameType, + ) -> Id<::SameType, Shared> { + unimplemented!() + } + + #[method(compare:)] + fn compare(&self, _other: &Self) -> bool { + unimplemented!() + } + + #[method_id(test4)] + #[allow(unused_parens)] + fn test4(_this: &<(Self) as GetSameType>::SameType) -> Id { + unimplemented!() + } + } +); + +fn main() {} diff --git a/crates/objc2/tests/no_prelude.rs b/crates/objc2/tests/no_prelude.rs index b6eac14f0..60cbad393 100644 --- a/crates/objc2/tests/no_prelude.rs +++ b/crates/objc2/tests/no_prelude.rs @@ -94,6 +94,11 @@ new_objc2::declare_class!( unsafe impl CustomObject { #[method(a)] fn _a() {} + + #[method_id(b)] + fn _b() -> new_objc2::rc::Id { + ::core::unimplemented!() + } } ); diff --git a/crates/test-ui/ui/declare_add_bad_method.rs b/crates/test-ui/ui/declare_add_bad_method.rs new file mode 100644 index 000000000..028f4245f --- /dev/null +++ b/crates/test-ui/ui/declare_add_bad_method.rs @@ -0,0 +1,38 @@ +use objc2::declare::ClassBuilder; +use objc2::rc::{Allocated, Id, Shared}; +use objc2::runtime::{Sel, NSObject}; +use objc2::{sel, ClassType}; + +fn main() { + let builder = ClassBuilder::new("DeclareClassTest", NSObject::class()).unwrap(); + + // Test receiver + unsafe { + fn foo(_obj: NSObject, _sel: Sel) {} + builder.add_method(sel!(foo), foo as fn(_, _)); + } + unsafe { + fn foo(_obj: Allocated, _sel: Sel) {} + builder.add_method(sel!(foo), foo as fn(_, _)); + } + + // Test return type + unsafe { + fn foo(_obj: &NSObject, _sel: Sel) -> bool { + unimplemented!() + } + builder.add_method(sel!(foo), foo as fn(_, _) -> _); + } + unsafe { + fn foo(_obj: &NSObject, _sel: Sel) -> Id { + unimplemented!() + } + builder.add_method(sel!(foo), foo as fn(_, _) -> _); + } + + // Test arguments + unsafe { + fn foo(_obj: &NSObject, _sel: Sel, item: bool) {} + builder.add_method(sel!(foo:), foo as fn(_, _, _)); + } +} diff --git a/crates/test-ui/ui/declare_add_bad_method.stderr b/crates/test-ui/ui/declare_add_bad_method.stderr new file mode 100644 index 000000000..e1bb491ad --- /dev/null +++ b/crates/test-ui/ui/declare_add_bad_method.stderr @@ -0,0 +1,119 @@ +error[E0277]: the trait bound `fn(_, _): MethodImplementation` is not satisfied + --> ui/declare_add_bad_method.rs + | + | builder.add_method(sel!(foo), foo as fn(_, _)); + | ---------- ^^^^^^^^^^^^^^^ the trait `MethodImplementation` is not implemented for `fn(_, _)` + | | + | required by a bound introduced by this call + | + = help: the following other types implement trait `MethodImplementation`: + extern "C" fn(&'a T, objc2::runtime::Sel) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D, E) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D, E, F) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D, E, F, G) -> R + and $N others +note: required by a bound in `ClassBuilder::add_method` + --> $WORKSPACE/crates/objc2/src/declare.rs + | + | F: MethodImplementation, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `ClassBuilder::add_method` + +error[E0277]: the trait bound `fn(_, _): MethodImplementation` is not satisfied + --> ui/declare_add_bad_method.rs + | + | builder.add_method(sel!(foo), foo as fn(_, _)); + | ---------- ^^^^^^^^^^^^^^^ the trait `MethodImplementation` is not implemented for `fn(_, _)` + | | + | required by a bound introduced by this call + | + = help: the following other types implement trait `MethodImplementation`: + extern "C" fn(&'a T, objc2::runtime::Sel) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D, E) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D, E, F) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D, E, F, G) -> R + and $N others +note: required by a bound in `ClassBuilder::add_method` + --> $WORKSPACE/crates/objc2/src/declare.rs + | + | F: MethodImplementation, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `ClassBuilder::add_method` + +error[E0277]: the trait bound `fn(_, _) -> _: MethodImplementation` is not satisfied + --> ui/declare_add_bad_method.rs + | + | builder.add_method(sel!(foo), foo as fn(_, _) -> _); + | ---------- ^^^^^^^^^^^^^^^^^^^^ the trait `MethodImplementation` is not implemented for `fn(_, _) -> _` + | | + | required by a bound introduced by this call + | + = help: the following other types implement trait `MethodImplementation`: + extern "C" fn(&'a T, objc2::runtime::Sel) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D, E) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D, E, F) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D, E, F, G) -> R + and $N others +note: required by a bound in `ClassBuilder::add_method` + --> $WORKSPACE/crates/objc2/src/declare.rs + | + | F: MethodImplementation, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `ClassBuilder::add_method` + +error[E0277]: the trait bound `fn(_, _) -> _: MethodImplementation` is not satisfied + --> ui/declare_add_bad_method.rs + | + | builder.add_method(sel!(foo), foo as fn(_, _) -> _); + | ---------- ^^^^^^^^^^^^^^^^^^^^ the trait `MethodImplementation` is not implemented for `fn(_, _) -> _` + | | + | required by a bound introduced by this call + | + = help: the following other types implement trait `MethodImplementation`: + extern "C" fn(&'a T, objc2::runtime::Sel) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D, E) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D, E, F) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D, E, F, G) -> R + and $N others +note: required by a bound in `ClassBuilder::add_method` + --> $WORKSPACE/crates/objc2/src/declare.rs + | + | F: MethodImplementation, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `ClassBuilder::add_method` + +error[E0277]: the trait bound `fn(_, _, _): MethodImplementation` is not satisfied + --> ui/declare_add_bad_method.rs + | + | builder.add_method(sel!(foo:), foo as fn(_, _, _)); + | ---------- ^^^^^^^^^^^^^^^^^^ the trait `MethodImplementation` is not implemented for `fn(_, _, _)` + | | + | required by a bound introduced by this call + | + = help: the following other types implement trait `MethodImplementation`: + extern "C" fn(&'a T, objc2::runtime::Sel) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D, E) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D, E, F) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D, E, F, G) -> R + and $N others +note: required by a bound in `ClassBuilder::add_method` + --> $WORKSPACE/crates/objc2/src/declare.rs + | + | F: MethodImplementation, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `ClassBuilder::add_method` diff --git a/crates/test-ui/ui/declare_class_invalid_receiver.rs b/crates/test-ui/ui/declare_class_invalid_receiver.rs index c6e440978..6fd22e240 100644 --- a/crates/test-ui/ui/declare_class_invalid_receiver.rs +++ b/crates/test-ui/ui/declare_class_invalid_receiver.rs @@ -1,4 +1,4 @@ -use objc2::rc::{Id, Shared}; +use objc2::rc::{Allocated, Id, Shared}; use objc2::{declare_class, ClassType}; use objc2::runtime::NSObject; @@ -25,6 +25,35 @@ declare_class!( unimplemented!() } } + + unsafe impl CustomObject { + #[method_id(test4)] + fn test4(self: Box) -> Id { + unimplemented!() + } + + #[method_id(test5)] + fn test5(this: Id) -> Id { + unimplemented!() + } + + #[method_id(test6)] + fn test6(this: Self) -> Id { + unimplemented!() + } + } + + unsafe impl CustomObject { + #[method_id(test7)] + fn test7(this: Allocated) -> Id { + unimplemented!() + } + + #[method_id(initTest8)] + fn test8(&self) -> Id { + unimplemented!() + } + } ); fn main() {} diff --git a/crates/test-ui/ui/declare_class_invalid_receiver.stderr b/crates/test-ui/ui/declare_class_invalid_receiver.stderr index b0b49723b..77b16a0a5 100644 --- a/crates/test-ui/ui/declare_class_invalid_receiver.stderr +++ b/crates/test-ui/ui/declare_class_invalid_receiver.stderr @@ -93,3 +93,207 @@ note: required by a bound in `ClassBuilder::add_method` | F: MethodImplementation, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `ClassBuilder::add_method` = note: this error originates in the macro `$crate::__declare_class_register_out` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `extern "C" fn(Box, objc2::runtime::Sel) -> __IdReturnValue: MethodImplementation` is not satisfied + --> ui/declare_class_invalid_receiver.rs + | + | / declare_class!( + | | struct CustomObject; + | | + | | unsafe impl ClassType for CustomObject { +... | + | | } + | | ); + | | ^ + | | | + | |_the trait `MethodImplementation` is not implemented for `extern "C" fn(Box, objc2::runtime::Sel) -> __IdReturnValue` + | required by a bound introduced by this call + | + = help: the following other types implement trait `MethodImplementation`: + extern "C" fn(&'a T, objc2::runtime::Sel) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D, E) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D, E, F) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D, E, F, G) -> R + and $N others +note: required by a bound in `ClassBuilder::add_method` + --> $WORKSPACE/crates/objc2/src/declare.rs + | + | F: MethodImplementation, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `ClassBuilder::add_method` + = note: this error originates in the macro `$crate::__declare_class_register_out` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `extern "C" fn(Id, objc2::runtime::Sel) -> __IdReturnValue: MethodImplementation` is not satisfied + --> ui/declare_class_invalid_receiver.rs + | + | / declare_class!( + | | struct CustomObject; + | | + | | unsafe impl ClassType for CustomObject { +... | + | | } + | | ); + | | ^ + | | | + | |_the trait `MethodImplementation` is not implemented for `extern "C" fn(Id, objc2::runtime::Sel) -> __IdReturnValue` + | required by a bound introduced by this call + | + = help: the following other types implement trait `MethodImplementation`: + extern "C" fn(&'a T, objc2::runtime::Sel) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D, E) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D, E, F) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D, E, F, G) -> R + and $N others +note: required by a bound in `ClassBuilder::add_method` + --> $WORKSPACE/crates/objc2/src/declare.rs + | + | F: MethodImplementation, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `ClassBuilder::add_method` + = note: this error originates in the macro `$crate::__declare_class_register_out` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `extern "C" fn(CustomObject, objc2::runtime::Sel) -> __IdReturnValue: MethodImplementation` is not satisfied + --> ui/declare_class_invalid_receiver.rs + | + | / declare_class!( + | | struct CustomObject; + | | + | | unsafe impl ClassType for CustomObject { +... | + | | } + | | ); + | | ^ + | | | + | |_the trait `MethodImplementation` is not implemented for `extern "C" fn(CustomObject, objc2::runtime::Sel) -> __IdReturnValue` + | required by a bound introduced by this call + | + = help: the following other types implement trait `MethodImplementation`: + extern "C" fn(&'a T, objc2::runtime::Sel) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D, E) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D, E, F) -> R + extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D, E, F, G) -> R + and $N others +note: required by a bound in `ClassBuilder::add_method` + --> $WORKSPACE/crates/objc2/src/declare.rs + | + | F: MethodImplementation, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `ClassBuilder::add_method` + = note: this error originates in the macro `$crate::__declare_class_register_out` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `Box: MessageReceiver` is not satisfied + --> ui/declare_class_invalid_receiver.rs + | + | / declare_class!( + | | struct CustomObject; + | | + | | unsafe impl ClassType for CustomObject { +... | + | | } + | | ); + | |_^ the trait `MessageReceiver` is not implemented for `Box` + | + = help: the following other types implement trait `MessageReceiver`: + &'a Id + &'a T + &'a mut Id + &'a mut T + &'a objc2::runtime::Class + *const T + *const objc2::runtime::Class + *mut T + and $N others + = note: required for `RetainSemantics<5>` to implement `MessageRecieveId, Id>` + = note: this error originates in the macro `$crate::__declare_class_method_out_inner` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `Id: MessageReceiver` is not satisfied + --> ui/declare_class_invalid_receiver.rs + | + | / declare_class!( + | | struct CustomObject; + | | + | | unsafe impl ClassType for CustomObject { +... | + | | } + | | ); + | |_^ the trait `MessageReceiver` is not implemented for `Id` + | + = help: the following other types implement trait `MessageReceiver`: + &'a Id + &'a mut Id + = note: required for `RetainSemantics<5>` to implement `MessageRecieveId, Id>` + = note: this error originates in the macro `$crate::__declare_class_method_out_inner` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `CustomObject: MessageReceiver` is not satisfied + --> ui/declare_class_invalid_receiver.rs + | + | / declare_class!( + | | struct CustomObject; + | | + | | unsafe impl ClassType for CustomObject { +... | + | | } + | | ); + | |_^ the trait `MessageReceiver` is not implemented for `CustomObject` + | + = help: the following other types implement trait `MessageReceiver`: + &'a Id + &'a T + &'a mut Id + &'a mut T + &'a objc2::runtime::Class + *const T + *const objc2::runtime::Class + *mut T + and $N others + = note: required for `RetainSemantics<5>` to implement `MessageRecieveId>` + = note: this error originates in the macro `$crate::__declare_class_method_out_inner` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `Allocated: MessageReceiver` is not satisfied + --> ui/declare_class_invalid_receiver.rs + | + | / declare_class!( + | | struct CustomObject; + | | + | | unsafe impl ClassType for CustomObject { +... | + | | } + | | ); + | |_^ the trait `MessageReceiver` is not implemented for `Allocated` + | + = help: the following other types implement trait `MessageReceiver`: + &'a Id + &'a T + &'a mut Id + &'a mut T + &'a objc2::runtime::Class + *const T + *const objc2::runtime::Class + *mut T + and $N others + = note: required for `RetainSemantics<5>` to implement `MessageRecieveId, Id>` + = note: this error originates in the macro `$crate::__declare_class_method_out_inner` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `RetainSemantics<3>: MessageRecieveId<&CustomObject, Id>` is not satisfied + --> ui/declare_class_invalid_receiver.rs + | + | / declare_class!( + | | struct CustomObject; + | | + | | unsafe impl ClassType for CustomObject { +... | + | | } + | | ); + | |_^ the trait `MessageRecieveId<&CustomObject, Id>` is not implemented for `RetainSemantics<3>` + | + = help: the trait `MessageRecieveId, Ret>` is implemented for `RetainSemantics<3>` + = note: this error originates in the macro `$crate::__declare_class_method_out_inner` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/crates/test-ui/ui/declare_class_invalid_syntax.rs b/crates/test-ui/ui/declare_class_invalid_syntax.rs index 4880b56f5..78e6e349e 100644 --- a/crates/test-ui/ui/declare_class_invalid_syntax.rs +++ b/crates/test-ui/ui/declare_class_invalid_syntax.rs @@ -1,5 +1,6 @@ use objc2::{declare_class, ClassType}; use objc2::runtime::NSObject; +use objc2::rc::{Id, Shared}; declare_class!( struct CustomObject; @@ -14,7 +15,7 @@ declare_class!( } #[method_id(testMethodId)] - fn test_method_id() { + fn test_method_id_no_return() { unimplemented!() } @@ -43,6 +44,33 @@ declare_class!( #[method(testNoBody)] fn test_no_body(&self); } + + unsafe impl CustomObject { + #[method_id(alloc)] + fn test_method_id_bad_selector1() -> Id { + unimplemented!() + } + + #[method_id(retain)] + fn test_method_id_bad_selector2() -> Id { + unimplemented!() + } + + #[method_id(release)] + fn test_method_id_bad_selector3() -> Id { + unimplemented!() + } + + #[method_id(autorelease)] + fn test_method_id_bad_selector4() -> Id { + unimplemented!() + } + + #[method_id(dealloc)] + fn test_method_id_bad_selector5() -> Id { + unimplemented!() + } + } ); fn main() {} diff --git a/crates/test-ui/ui/declare_class_invalid_syntax.stderr b/crates/test-ui/ui/declare_class_invalid_syntax.stderr index b5f1bdc89..d7752537f 100644 --- a/crates/test-ui/ui/declare_class_invalid_syntax.stderr +++ b/crates/test-ui/ui/declare_class_invalid_syntax.stderr @@ -12,20 +12,6 @@ error: must specify the desired selector using `#[method(...)]` or `#[method_id( | = note: this error originates in the macro `$crate::__extract_custom_attributes_inner` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) -error: `#[method_id(...)]` is not supported in `declare_class!` yet - --> ui/declare_class_invalid_syntax.rs - | - | / declare_class!( - | | struct CustomObject; - | | - | | unsafe impl ClassType for CustomObject { -... | - | | } - | | ); - | |_^ - | - = note: this error originates in the macro `$crate::__declare_class_register_out` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) - error: expected expression, found `}` --> ui/declare_class_invalid_syntax.rs | @@ -76,6 +62,76 @@ note: while trying to match meta-variable `$body:block` | fn $name:ident($($args:tt)*) $(-> $ret:ty)? $body:block | ^^^^^^^^^^^ +error: `#[method_id(alloc)]` is not supported. Use `#[method(alloc)]` and do the memory management yourself + --> ui/declare_class_invalid_syntax.rs + | + | / declare_class!( + | | struct CustomObject; + | | + | | unsafe impl ClassType for CustomObject { +... | + | | } + | | ); + | |_^ + | + = note: this error originates in the macro `$crate::__get_method_id_sel` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `#[method_id(retain)]` is not supported. Use `#[method(retain)]` and do the memory management yourself + --> ui/declare_class_invalid_syntax.rs + | + | / declare_class!( + | | struct CustomObject; + | | + | | unsafe impl ClassType for CustomObject { +... | + | | } + | | ); + | |_^ + | + = note: this error originates in the macro `$crate::__get_method_id_sel` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `#[method_id(release)]` is not supported. Use `#[method(release)]` and do the memory management yourself + --> ui/declare_class_invalid_syntax.rs + | + | / declare_class!( + | | struct CustomObject; + | | + | | unsafe impl ClassType for CustomObject { +... | + | | } + | | ); + | |_^ + | + = note: this error originates in the macro `$crate::__get_method_id_sel` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `#[method_id(autorelease)]` is not supported. Use `#[method(autorelease)]` and do the memory management yourself + --> ui/declare_class_invalid_syntax.rs + | + | / declare_class!( + | | struct CustomObject; + | | + | | unsafe impl ClassType for CustomObject { +... | + | | } + | | ); + | |_^ + | + = note: this error originates in the macro `$crate::__get_method_id_sel` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `#[method_id(dealloc)]` is not supported. Add an instance variable with a `Drop` impl to the class instead + --> ui/declare_class_invalid_syntax.rs + | + | / declare_class!( + | | struct CustomObject; + | | + | | unsafe impl ClassType for CustomObject { +... | + | | } + | | ); + | |_^ + | + = note: this error originates in the macro `$crate::__get_method_id_sel` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) + error: must specify the desired selector using `#[method(...)]` or `#[method_id(...)]` --> ui/declare_class_invalid_syntax.rs | @@ -90,6 +146,20 @@ error: must specify the desired selector using `#[method(...)]` or `#[method_id( | = note: this error originates in the macro `$crate::__extract_custom_attributes_inner` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) +error: `#[method_id(...)]` must have a return type + --> ui/declare_class_invalid_syntax.rs + | + | / declare_class!( + | | struct CustomObject; + | | + | | unsafe impl ClassType for CustomObject { +... | + | | } + | | ); + | |_^ + | + = note: this error originates in the macro `$crate::__declare_class_method_out_inner` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) + error: no rules expected the token `(` --> ui/declare_class_invalid_syntax.rs | @@ -150,3 +220,22 @@ error[E0599]: no function or associated item named `test_self` found for struct | | } | | ); | |_- function or associated item `test_self` not found for this struct + +error[E0277]: the trait bound `RetainSemantics<2>: MessageRecieveId<&objc2::runtime::Class, Id>` is not satisfied + --> ui/declare_class_invalid_syntax.rs + | + | / declare_class!( + | | struct CustomObject; + | | + | | unsafe impl ClassType for CustomObject { +... | + | | } + | | ); + | |_^ the trait `MessageRecieveId<&objc2::runtime::Class, Id>` is not implemented for `RetainSemantics<2>` + | + = help: the following other types implement trait `MessageRecieveId`: + as MessageRecieveId> + as MessageRecieveId, Ret>> + as MessageRecieveId> + as MessageRecieveId> + = note: this error originates in the macro `$crate::__declare_class_method_out_inner` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/crates/test-ui/ui/declare_class_invalid_type2.rs b/crates/test-ui/ui/declare_class_invalid_type2.rs new file mode 100644 index 000000000..593f351bc --- /dev/null +++ b/crates/test-ui/ui/declare_class_invalid_type2.rs @@ -0,0 +1,25 @@ +use objc2::rc::{Allocated, Id, Shared}; +use objc2::{declare_class, ClassType}; +use objc2::runtime::NSObject; + +declare_class!( + struct CustomObject; + + unsafe impl ClassType for CustomObject { + type Super = NSObject; + } + + unsafe impl CustomObject { + #[method_id(initNotSameGenerics)] + fn test_init_not_same_generics(this: Allocated) -> Id { + unimplemented!() + } + + #[method_id(methodIdNotId)] + fn test_method_id_not_id(&self) -> i32 { + unimplemented!() + } + } +); + +fn main() {} diff --git a/crates/test-ui/ui/declare_class_invalid_type2.stderr b/crates/test-ui/ui/declare_class_invalid_type2.stderr new file mode 100644 index 000000000..af04be73c --- /dev/null +++ b/crates/test-ui/ui/declare_class_invalid_type2.stderr @@ -0,0 +1,34 @@ +error[E0271]: type mismatch resolving ` as MaybeUnwrap>::Input == Id` + --> ui/declare_class_invalid_type2.rs + | + | / declare_class!( + | | struct CustomObject; + | | + | | unsafe impl ClassType for CustomObject { +... | + | | } + | | ); + | |_^ expected struct `CustomObject`, found struct `NSObject` + | + = note: expected struct `Id` + found struct `Id` + = note: required for `RetainSemantics<3>` to implement `MessageRecieveId, Id>` + = note: this error originates in the macro `$crate::__declare_class_method_out_inner` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `i32: MaybeOptionId` is not satisfied + --> ui/declare_class_invalid_type2.rs + | + | / declare_class!( + | | struct CustomObject; + | | + | | unsafe impl ClassType for CustomObject { +... | + | | } + | | ); + | |_^ the trait `MaybeOptionId` is not implemented for `i32` + | + = help: the following other types implement trait `MaybeOptionId`: + Id + Option> + = note: required for `RetainSemantics<5>` to implement `MessageRecieveId<&CustomObject, i32>` + = note: this error originates in the macro `$crate::__declare_class_method_out_inner` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/crates/test-ui/ui/wrong_optional.rs b/crates/test-ui/ui/wrong_optional.rs index 2ca6e3893..7523ad4ac 100644 --- a/crates/test-ui/ui/wrong_optional.rs +++ b/crates/test-ui/ui/wrong_optional.rs @@ -29,22 +29,34 @@ extern_methods!( ); declare_class!( - struct CustomObject; + struct CustomObject1; - unsafe impl ClassType for CustomObject { + unsafe impl ClassType for CustomObject1 { type Super = NSObject; } - unsafe impl CustomObject { + unsafe impl CustomObject1 { #[method(c)] #[optional] /// Doc comment fn c(&self) {} + } +); + +declare_class!( + struct CustomObject2; + + unsafe impl ClassType for CustomObject2 { + type Super = NSObject; + } + unsafe impl CustomObject2 { #[optional] /// Doc comment #[method_id(d)] - fn d(&self) {} + fn d(&self) -> Id { + unimplemented!() + } } ); diff --git a/crates/test-ui/ui/wrong_optional.stderr b/crates/test-ui/ui/wrong_optional.stderr index 5ec4ab9ec..2d3e920ab 100644 --- a/crates/test-ui/ui/wrong_optional.stderr +++ b/crates/test-ui/ui/wrong_optional.stderr @@ -30,9 +30,9 @@ error: `#[optional]` is only supported in `extern_protocol!` --> ui/wrong_optional.rs | | / declare_class!( - | | struct CustomObject; + | | struct CustomObject1; | | - | | unsafe impl ClassType for CustomObject { + | | unsafe impl ClassType for CustomObject1 { ... | | | } | | ); @@ -40,13 +40,13 @@ error: `#[optional]` is only supported in `extern_protocol!` | = note: this error originates in the macro `$crate::__declare_class_register_out` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) -error: `#[method_id(...)]` is not supported in `declare_class!` yet +error: `#[optional]` is only supported in `extern_protocol!` --> ui/wrong_optional.rs | | / declare_class!( - | | struct CustomObject; + | | struct CustomObject2; | | - | | unsafe impl ClassType for CustomObject { + | | unsafe impl ClassType for CustomObject2 { ... | | | } | | );