Skip to content

Commit 09d117d

Browse files
authored
Merge pull request #336 from madsmtm/multiple-colons-in-selectors
Allow multiple colons in selectors
2 parents e08964a + 9cc6288 commit 09d117d

24 files changed

+922
-215
lines changed

crates/header-translator/translation-config.toml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -774,11 +774,6 @@ skipped = true
774774
[class.NSMutableData.methods.mutableBytes]
775775
skipped = true
776776

777-
# Selector contains multiple colons after each other, like `::::`
778-
[class.CAMediaTimingFunction.methods]
779-
functionWithControlPoints = { skipped = true }
780-
initWithControlPoints = { skipped = true }
781-
782777
# Uses __autoreleasing in a typedef, which I'm unsure how to handle
783778
[typedef.MTLAutoreleasedArgument]
784779
skipped = true

crates/icrate/src/generated

crates/objc2-proc-macros/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
66

77
## Unreleased - YYYY-MM-DD
88

9+
### Fixed
10+
* Allow all types of tokens in internal macro.
11+
912

1013
## 0.1.0 - 2022-07-19
1114

crates/objc2-proc-macros/src/lib.rs

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -24,27 +24,19 @@ use proc_macro::Literal;
2424
use proc_macro::TokenStream;
2525
use proc_macro::TokenTree;
2626

27-
/// Quick n' dirty way to extract the idents given by the `sel!` macro.
27+
/// Extract all identifiers in the given tokenstream.
2828
fn get_idents(input: TokenStream) -> impl Iterator<Item = Ident> {
2929
input.into_iter().flat_map(|token| {
30-
if let TokenTree::Group(group) = token {
31-
group
32-
.stream()
33-
.into_iter()
34-
.map(|token| {
35-
if let TokenTree::Ident(ident) = token {
36-
ident
37-
} else {
38-
panic!("Expected ident, got {token:?}")
39-
}
40-
})
41-
.collect::<Vec<_>>()
42-
.into_iter()
43-
} else if let TokenTree::Ident(ident) = token {
44-
vec![ident].into_iter()
45-
} else {
46-
panic!("Expected group or ident, got {token:?}")
30+
match token {
31+
TokenTree::Group(group) => get_idents(group.stream()).collect::<Vec<_>>(),
32+
TokenTree::Ident(ident) => {
33+
vec![ident]
34+
}
35+
TokenTree::Punct(_) | TokenTree::Literal(_) => {
36+
vec![]
37+
}
4738
}
39+
.into_iter()
4840
})
4941
}
5042

crates/objc2/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
88

99
### Added
1010
* Support `#[cfg(...)]` attributes in `extern_class!` macro.
11+
* Added support for selectors with multiple colons like `abc::` in the `sel!`,
12+
`extern_class!`, `extern_protocol!` and `declare_class!` macros.
1113

1214
### Changed
1315
* **BREAKING**: Using the automatic `NSError**`-to-`Result` functionality in

crates/objc2/src/__macro_helpers.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -860,12 +860,14 @@ mod tests {
860860

861861
#[test]
862862
fn test_in_selector_family() {
863+
#[track_caller]
863864
fn assert_in_family(selector: &str, family: &str) {
864865
assert!(in_selector_family(selector.as_bytes(), family.as_bytes()));
865866
let selector = selector.to_string() + "\0";
866867
assert!(in_selector_family(selector.as_bytes(), family.as_bytes()));
867868
}
868869

870+
#[track_caller]
869871
fn assert_not_in_family(selector: &str, family: &str) {
870872
assert!(!in_selector_family(selector.as_bytes(), family.as_bytes()));
871873
let selector = selector.to_string() + "\0";
@@ -946,6 +948,13 @@ mod tests {
946948
assert_not_in_family("a", "");
947949
assert_in_family("_A", "");
948950
assert_in_family("A", "");
951+
952+
// Double-colon selectors
953+
assert_in_family("abc::abc::", "abc");
954+
assert_in_family("abc:::", "abc");
955+
assert_in_family("abcDef::xyz:", "abc");
956+
// Invalid selector (probably)
957+
assert_not_in_family("::abc:", "abc");
949958
}
950959

951960
mod test_trait_disambugated {

crates/objc2/src/declare.rs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -229,10 +229,6 @@ method_decl_impl!(A, B, C, D, E, F, G, H, I, J);
229229
method_decl_impl!(A, B, C, D, E, F, G, H, I, J, K);
230230
method_decl_impl!(A, B, C, D, E, F, G, H, I, J, K, L);
231231

232-
fn count_args(sel: Sel) -> usize {
233-
sel.name().chars().filter(|&c| c == ':').count()
234-
}
235-
236232
fn method_type_encoding(ret: &Encoding, args: &[Encoding]) -> CString {
237233
// First two arguments are always self and the selector
238234
let mut types = format!("{ret}{}{}", <*mut Object>::ENCODING, Sel::ENCODING);
@@ -360,7 +356,7 @@ impl ClassBuilder {
360356
let enc_args = F::Args::ENCODINGS;
361357
let enc_ret = F::Ret::ENCODING;
362358

363-
let sel_args = count_args(sel);
359+
let sel_args = sel.number_of_arguments();
364360
assert_eq!(
365361
sel_args,
366362
enc_args.len(),
@@ -417,7 +413,7 @@ impl ClassBuilder {
417413
let enc_args = F::Args::ENCODINGS;
418414
let enc_ret = F::Ret::ENCODING;
419415

420-
let sel_args = count_args(sel);
416+
let sel_args = sel.number_of_arguments();
421417
assert_eq!(
422418
sel_args,
423419
enc_args.len(),
@@ -575,7 +571,7 @@ impl ProtocolBuilder {
575571
Ret: Encode,
576572
{
577573
let encs = Args::ENCODINGS;
578-
let sel_args = count_args(sel);
574+
let sel_args = sel.number_of_arguments();
579575
assert_eq!(
580576
sel_args,
581577
encs.len(),

crates/objc2/src/declare/declare_class_tests.rs

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#![deny(deprecated)]
2-
use crate::rc::{Id, Owned};
2+
use core::ptr;
3+
4+
use crate::rc::{Id, Owned, Shared};
35
use crate::runtime::NSObject;
46
use crate::{declare_class, extern_methods, sel, ClassType};
57

@@ -173,3 +175,74 @@ fn test_method_that_is_never_available() {
173175
assert!(!cls.responds_to(sel!(never)));
174176
assert!(!metacls.responds_to(sel!(never)));
175177
}
178+
179+
declare_class!(
180+
struct TestMultipleColonSelector;
181+
182+
unsafe impl ClassType for TestMultipleColonSelector {
183+
type Super = NSObject;
184+
}
185+
186+
unsafe impl TestMultipleColonSelector {
187+
#[method(test::arg3:)]
188+
fn _test_class(arg1: i32, arg2: i32, arg3: i32) -> i32 {
189+
arg1 + arg2 + arg3
190+
}
191+
192+
#[method(test::arg3:)]
193+
fn _test_instance(&self, arg1: i32, arg2: i32, arg3: i32) -> i32 {
194+
arg1 * arg2 * arg3
195+
}
196+
197+
#[method(test::error:)]
198+
fn _test_error(&self, _arg1: i32, _arg2: i32, _arg3: *mut *mut NSObject) -> bool {
199+
true
200+
}
201+
202+
#[method(test:::withObject:)]
203+
fn _test_object(
204+
&self,
205+
_arg1: i32,
206+
_arg2: i32,
207+
_arg3: i32,
208+
_obj: *const Self,
209+
) -> *const Self {
210+
ptr::null()
211+
}
212+
}
213+
);
214+
215+
extern_methods!(
216+
unsafe impl TestMultipleColonSelector {
217+
#[method_id(new)]
218+
fn new() -> Id<Self, Owned>;
219+
220+
#[method(test::arg3:)]
221+
fn test_class(arg1: i32, arg2: i32, arg3: i32) -> i32;
222+
223+
#[method(test::arg3:)]
224+
fn test_instance(&self, arg1: i32, arg2: i32, arg3: i32) -> i32;
225+
226+
#[method(test::error:_)]
227+
fn test_error(&self, arg1: i32, arg2: i32) -> Result<(), Id<NSObject, Shared>>;
228+
229+
#[method_id(test:::withObject:)]
230+
fn test_object(
231+
&self,
232+
arg1: i32,
233+
arg2: i32,
234+
arg3: i32,
235+
obj: *const Self,
236+
) -> Option<Id<Self, Shared>>;
237+
}
238+
);
239+
240+
#[test]
241+
fn test_multiple_colon_selector() {
242+
assert_eq!(TestMultipleColonSelector::test_class(2, 3, 4), 9);
243+
244+
let obj = TestMultipleColonSelector::new();
245+
assert_eq!(obj.test_instance(1, 2, 3), 6);
246+
assert!(obj.test_error(1, 2).is_ok());
247+
assert!(obj.test_object(1, 2, 3, ptr::null()).is_none());
248+
}

crates/objc2/src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,9 @@ pub use objc2_proc_macros::__hash_idents;
207207
#[macro_export]
208208
macro_rules! __hash_idents {
209209
// Noop; used to make our other macros a bit easier to read
210-
($($x:tt)*) => {$($x)*};
210+
($($x:tt)*) => {
211+
()
212+
};
211213
}
212214

213215
#[doc(hidden)]

crates/objc2/src/macros.rs

Lines changed: 76 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
mod __attribute_helpers;
2+
mod __method_msg_send;
23
mod __msg_send_parse;
34
mod __rewrite_self_arg;
45
mod declare_class;
@@ -166,6 +167,19 @@ macro_rules! __class_inner {
166167
/// let sel = sel!(aSelector:withoutTrailingColon);
167168
/// ```
168169
///
170+
/// A selector with internal colons:
171+
///
172+
/// ```
173+
/// # use objc2::sel;
174+
/// let sel = sel!(sel::with:::multiple:internal::::colons:::);
175+
///
176+
/// // Yes, that is possible! The following Objective-C would work:
177+
/// //
178+
/// // @interface MyThing: NSObject
179+
/// // + (void)test:(int)a :(int)b arg:(int)c :(int)d;
180+
/// // @end
181+
/// ```
182+
///
169183
/// Unsupported usage that you may run into when using macros - fails to
170184
/// compile when the `"unstable-static-sel"` feature is enabled.
171185
///
@@ -196,21 +210,71 @@ macro_rules! sel {
196210
(new) => ({
197211
$crate::__macro_helpers::new_sel()
198212
});
199-
($first:ident $(: $($rest:ident :)*)?) => ({
213+
($sel:ident) => ({
200214
$crate::__sel_inner!(
201-
$crate::__sel_data!($first $(: $($rest :)*)?),
202-
$crate::__hash_idents!($first $($($rest)*)?)
215+
$crate::__sel_data!($sel),
216+
$crate::__hash_idents!($sel)
203217
)
204218
});
219+
($($sel:ident :)*) => ({
220+
$crate::__sel_inner!(
221+
$crate::__sel_data!($($sel :)*),
222+
$crate::__hash_idents!($($sel :)*)
223+
)
224+
});
225+
($($sel:tt)*) => {
226+
$crate::__sel_inner!(
227+
$crate::__sel_helper! {
228+
@()
229+
$($sel)*
230+
},
231+
$crate::__hash_idents!($($sel)*)
232+
)
233+
};
234+
}
235+
236+
/// Handle selectors with internal colons.
237+
///
238+
/// Required since `::` is a different token than `:`.
239+
#[doc(hidden)]
240+
#[macro_export]
241+
macro_rules! __sel_helper {
242+
// Base-case
243+
{
244+
@($($parsed_sel:tt)*)
245+
} => ({
246+
$crate::__sel_data!($($parsed_sel)*)
247+
});
248+
// Parse identitifer + colon token
249+
{
250+
@($($parsed_sel:tt)*)
251+
$($ident:ident)? : $($rest:tt)*
252+
} => {
253+
$crate::__sel_helper! {
254+
@($($parsed_sel)* $($ident)? :)
255+
$($rest)*
256+
}
257+
};
258+
// Parse identitifer + path separator token
259+
{
260+
@($($parsed_sel:tt)*)
261+
$($ident:ident)? :: $($rest:tt)*
262+
} => {
263+
$crate::__sel_helper! {
264+
// Notice space between these
265+
@($($parsed_sel)* $($ident)? : :)
266+
$($rest)*
267+
}
268+
};
205269
}
206270

207271
#[doc(hidden)]
208272
#[macro_export]
209273
macro_rules! __sel_data {
210-
($first:ident $(: $($rest:ident :)*)?) => {
274+
($first:ident $(: $($($rest:ident)? :)*)?) => {
211275
$crate::__macro_helpers::concat!(
212276
$crate::__macro_helpers::stringify!($first),
213-
$(':', $($crate::__macro_helpers::stringify!($rest), ':',)*)?
277+
$(':', $($($crate::__macro_helpers::stringify!($rest),)? ':',)*)?
214278
'\0',
215279
)
216280
};
@@ -1085,20 +1149,6 @@ macro_rules! msg_send_id {
10851149
result = <$crate::__macro_helpers::Init as $crate::__macro_helpers::MsgSendId<_, _>>::send_message_id($obj, sel, ());
10861150
result
10871151
});
1088-
[$obj:expr, @__retain_semantics $retain_semantics:ident $($selector_and_arguments:tt)+] => {
1089-
$crate::__msg_send_parse! {
1090-
($crate::__msg_send_id_helper)
1091-
@(send_message_id_error)
1092-
@()
1093-
@()
1094-
@($($selector_and_arguments)+)
1095-
@(send_message_id)
1096-
1097-
@($obj)
1098-
@($retain_semantics)
1099-
}
1100-
// compile_error!(stringify!($($selector_and_arguments)*))
1101-
};
11021152
[$obj:expr, $($selector_and_arguments:tt)+] => {
11031153
$crate::__msg_send_parse! {
11041154
($crate::__msg_send_id_helper)
@@ -1166,32 +1216,34 @@ macro_rules! __msg_send_id_helper {
11661216
@($fn:ident)
11671217
@($obj:expr)
11681218
@($retain_semantics:ident)
1169-
@($sel_first:ident $(: $($sel_rest:ident :)*)?)
1219+
@($($selector:tt)*)
11701220
@($($argument:expr,)*)
11711221
} => ({
11721222
<$crate::__macro_helpers::$retain_semantics as $crate::__macro_helpers::MsgSendId<_, _>>::$fn::<_, _>(
11731223
$obj,
1174-
$crate::sel!($sel_first $(: $($sel_rest :)*)?),
1224+
$crate::sel!($($selector)*),
11751225
($($argument,)*),
11761226
)
11771227
});
11781228
{
11791229
@($fn:ident)
11801230
@($obj:expr)
11811231
@()
1182-
@($sel_first:ident $(: $($sel_rest:ident :)*)?)
1232+
@($($selector:tt)*)
11831233
@($($argument:expr,)*)
11841234
} => ({
11851235
// Don't use `sel!`, otherwise we'd end up with defining this data twice.
1186-
const __SELECTOR_DATA: &$crate::__macro_helpers::str = $crate::__sel_data!($sel_first $(: $($sel_rest :)*)?);
1236+
const __SELECTOR_DATA: &$crate::__macro_helpers::str = $crate::__sel_data!(
1237+
$($selector)*
1238+
);
11871239
let result;
11881240
result = <$crate::__macro_helpers::RetainSemantics<{
11891241
$crate::__macro_helpers::retain_semantics(__SELECTOR_DATA)
11901242
}> as $crate::__macro_helpers::MsgSendId<_, _>>::$fn::<_, _>(
11911243
$obj,
11921244
$crate::__sel_inner!(
11931245
__SELECTOR_DATA,
1194-
$crate::__hash_idents!($sel_first $($($sel_rest)*)?)
1246+
$crate::__hash_idents!($($selector)*)
11951247
),
11961248
($($argument,)*),
11971249
);

0 commit comments

Comments
 (0)