From 25a6dd6e99af2d80592767b99cb268dce4702457 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sat, 7 Sep 2024 00:16:46 +0200 Subject: [PATCH] Require counterparts to be set when implementing NSMutableCopying This allows us to easily see and fix the missing counterparts. --- crates/header-translator/src/stmt.rs | 62 ++++++++++++------- .../ui/ns_copying_without_copy_helper.stderr | 6 +- .../ui/protocol_object_only_protocols.stderr | 2 +- .../objc2-app-kit/translation-config.toml | 6 ++ .../objc2-contacts/translation-config.toml | 8 +++ .../objc2-core-wlan/translation-config.toml | 6 ++ .../objc2-foundation/translation-config.toml | 1 + .../objc2-store-kit/translation-config.toml | 4 ++ .../objc2-ui-kit/translation-config.toml | 10 +++ .../translation-config.toml | 3 + generated | 2 +- 11 files changed, 84 insertions(+), 26 deletions(-) diff --git a/crates/header-translator/src/stmt.rs b/crates/header-translator/src/stmt.rs index d194025a5..040fb9af8 100644 --- a/crates/header-translator/src/stmt.rs +++ b/crates/header-translator/src/stmt.rs @@ -289,15 +289,9 @@ pub(crate) fn items_required_by_decl( match entity.get_kind() { EntityKind::ObjCInterfaceDecl => { - let data = context.library(id.library_name()).class_data.get(&id.name); - for (superclass, _, _) in parse_superclasses(entity, context) { items.push(superclass); } - if let Some(Counterpart::MutableSubclass(subclass)) = data.map(|data| &data.counterpart) - { - items.push(subclass.clone()); - } } EntityKind::ObjCProtocolDecl => { for entity in parse_direct_protocols(entity, context) { @@ -1965,8 +1959,35 @@ impl Stmt { .map_name(|_| "MutableCopyingHelper".to_string()) }; + let mut required_items = self.required_items(); + + // Assume counterparts have the same generics. + let ty = match (cls_counterpart, &*protocol.name) { + (Counterpart::ImmutableSuperclass(superclass), "NSCopying") => { + required_items.push(superclass.clone()); + format!( + "{}{}", + superclass.path_in_relation_to(id), + GenericTyHelper(generics) + ) + } + (Counterpart::MutableSubclass(subclass), "NSMutableCopying") => { + required_items.push(subclass.clone()); + format!( + "{}{}", + subclass.path_in_relation_to(id), + GenericTyHelper(generics) + ) + } + _ => "Self".into(), + }; + writeln!(f)?; - write!(f, "{}", self.cfg_gate_ln(config))?; + write!( + f, + "{}", + cfg_gate_ln(required_items, [self.location()], config, self.location()) + )?; writeln!( f, "unsafe impl{} {} for {}{} {{", @@ -1976,23 +1997,22 @@ impl Stmt { GenericTyHelper(generics), )?; - write!(f, " type Result = ")?; - // Assume counterparts have the same generics. - match (cls_counterpart, &*protocol.name) { - (Counterpart::ImmutableSuperclass(superclass), "NSCopying") => { - write!(f, "{}", superclass.path_in_relation_to(id))?; - write!(f, "{}", GenericTyHelper(generics))?; - } - (Counterpart::MutableSubclass(subclass), "NSMutableCopying") => { - write!(f, "{}", subclass.path_in_relation_to(id))?; - write!(f, "{}", GenericTyHelper(generics))?; - } - _ => write!(f, "Self")?, - } - writeln!(f, ";")?; + writeln!(f, " type Result = {ty};")?; writeln!(f, "}}")?; } + + if protocol.name == "NSMutableCopying" + && *cls_counterpart == Counterpart::NoCounterpart + { + error!( + ?cls, + "Class implements NSMutableCopying, \ + but does not have a counterpart. You should \ + define the counterpart for this class, or \ + ignore the NSMutableCopying implementation" + ); + } } Self::ProtocolDecl { id, diff --git a/crates/test-ui/ui/ns_copying_without_copy_helper.stderr b/crates/test-ui/ui/ns_copying_without_copy_helper.stderr index 1c443f471..e2e97702f 100644 --- a/crates/test-ui/ui/ns_copying_without_copy_helper.stderr +++ b/crates/test-ui/ui/ns_copying_without_copy_helper.stderr @@ -6,13 +6,13 @@ error[E0277]: the trait bound `MyObj: CopyingHelper` is not satisfied | = help: the following other types implement trait `CopyingHelper`: NSArray - NSCountedSet NSDictionary NSError NSMutableArray NSMutableDictionary NSMutableSet NSMutableString + NSNotification and $N others error[E0277]: the trait bound `MyObj: MutableCopyingHelper` is not satisfied @@ -23,11 +23,11 @@ error[E0277]: the trait bound `MyObj: MutableCopyingHelper` is not satisfied | = help: the following other types implement trait `MutableCopyingHelper`: NSArray - NSCountedSet NSDictionary NSMutableArray NSMutableDictionary NSMutableSet NSMutableString NSSet - and $N others + NSString + ProtocolObject

diff --git a/crates/test-ui/ui/protocol_object_only_protocols.stderr b/crates/test-ui/ui/protocol_object_only_protocols.stderr index 43f2a3762..c0dffa165 100644 --- a/crates/test-ui/ui/protocol_object_only_protocols.stderr +++ b/crates/test-ui/ui/protocol_object_only_protocols.stderr @@ -267,13 +267,13 @@ error[E0277]: the trait bound `NSObject: NSCopying` is not satisfied | = help: the following other types implement trait `NSCopying`: NSArray - NSCountedSet NSDictionary NSError NSMutableArray NSMutableDictionary NSMutableSet NSMutableString + NSNotification and $N others = note: required for `dyn NSCopying` to implement `ImplementedBy` note: required by a bound in `ProtocolObject::

::from_ref` diff --git a/framework-crates/objc2-app-kit/translation-config.toml b/framework-crates/objc2-app-kit/translation-config.toml index 6bb17c4a4..bcbad4097 100644 --- a/framework-crates/objc2-app-kit/translation-config.toml +++ b/framework-crates/objc2-app-kit/translation-config.toml @@ -23,6 +23,12 @@ static.NSApp.skipped = true # These protocol impls would return the wrong types class.NSTextStorage.skipped-protocols = ["NSCopying", "NSMutableCopying"] +# Set counterparts +class.NSFontCollection.counterpart = "MutableSubclass(AppKit::NSFontCollection::NSMutableFontCollection)" +class.NSMutableFontCollection.counterpart = "ImmutableSuperclass(AppKit::NSFontCollection::NSFontCollection)" +class.NSParagraphStyle.counterpart = "MutableSubclass(AppKit::NSParagraphStyle::NSMutableParagraphStyle)" +class.NSMutableParagraphStyle.counterpart = "ImmutableSuperclass(AppKit::NSParagraphStyle::NSParagraphStyle)" + # Typedef that uses a generic from a class typedef.NSCollectionViewDiffableDataSourceItemProvider.skipped = true class.NSCollectionViewDiffableDataSource.methods."initWithCollectionView:itemProvider:".skipped = true diff --git a/framework-crates/objc2-contacts/translation-config.toml b/framework-crates/objc2-contacts/translation-config.toml index c434e6371..05624f4be 100644 --- a/framework-crates/objc2-contacts/translation-config.toml +++ b/framework-crates/objc2-contacts/translation-config.toml @@ -6,3 +6,11 @@ maccatalyst = "13.0" ios = "9.0" watchos = "2.0" visionos = "1.0" + +# Set counterparts +class.CNContact.counterpart = "MutableSubclass(Contacts::CNMutableContact::CNMutableContact)" +class.CNMutableContact.counterpart = "ImmutableSuperclass(Contacts::CNContact::CNContact)" +class.CNGroup.counterpart = "MutableSubclass(Contacts::CNMutableGroup::CNMutableGroup)" +class.CNMutableGroup.counterpart = "ImmutableSuperclass(Contacts::CNGroup::CNGroup)" +class.CNPostalAddress.counterpart = "MutableSubclass(Contacts::CNMutablePostalAddress::CNMutablePostalAddress)" +class.CNMutablePostalAddress.counterpart = "ImmutableSuperclass(Contacts::CNPostalAddress::CNPostalAddress)" diff --git a/framework-crates/objc2-core-wlan/translation-config.toml b/framework-crates/objc2-core-wlan/translation-config.toml index 2c9736268..4b3c73ace 100644 --- a/framework-crates/objc2-core-wlan/translation-config.toml +++ b/framework-crates/objc2-core-wlan/translation-config.toml @@ -4,6 +4,12 @@ required-dependencies = ["objc2-foundation"] macos = "10.6" maccatalyst = "13.0" +# Set counterparts +class.CWConfiguration.counterpart = "MutableSubclass(CoreWLAN::CWConfiguration::CWMutableConfiguration)" +class.CWMutableConfiguration.counterpart = "ImmutableSuperclass(CoreWLAN::CWConfiguration::CWConfiguration)" +class.CWNetworkProfile.counterpart = "MutableSubclass(CoreWLAN::CWNetworkProfile::CWMutableNetworkProfile)" +class.CWMutableNetworkProfile.counterpart = "ImmutableSuperclass(CoreWLAN::CWNetworkProfile::CWNetworkProfile)" + # Uses types from CoreFoundation fn.CWKeychainCopyEAPUsernameAndPassword.skipped = true fn.CWKeychainCopyEAPIdentityList.skipped = true diff --git a/framework-crates/objc2-foundation/translation-config.toml b/framework-crates/objc2-foundation/translation-config.toml index 7e5d2183c..e64b1c372 100644 --- a/framework-crates/objc2-foundation/translation-config.toml +++ b/framework-crates/objc2-foundation/translation-config.toml @@ -89,6 +89,7 @@ class.NSNumber.methods.new.skipped = true class.NSSimpleCString.skipped-protocols = ["NSCopying", "NSMutableCopying"] class.NSConstantString.skipped-protocols = ["NSCopying", "NSMutableCopying"] class.NSPurgeableData.skipped-protocols = ["NSCopying", "NSMutableCopying"] +class.NSCountedSet.skipped-protocols = ["NSCopying", "NSMutableCopying"] # Custom implementation for now struct._NSRange.skipped = true diff --git a/framework-crates/objc2-store-kit/translation-config.toml b/framework-crates/objc2-store-kit/translation-config.toml index 83c836b8b..902758b93 100644 --- a/framework-crates/objc2-store-kit/translation-config.toml +++ b/framework-crates/objc2-store-kit/translation-config.toml @@ -7,3 +7,7 @@ ios = "3.0" tvos = "9.0" watchos = "6.2" visionos = "1.0" + +# Set counterparts +class.SKPayment.counterpart = "MutableSubclass(StoreKit::SKPayment::SKMutablePayment)" +class.SKMutablePayment.counterpart = "ImmutableSuperclass(StoreKit::SKPayment::SKPayment)" diff --git a/framework-crates/objc2-ui-kit/translation-config.toml b/framework-crates/objc2-ui-kit/translation-config.toml index 2d9355f74..913b374b8 100644 --- a/framework-crates/objc2-ui-kit/translation-config.toml +++ b/framework-crates/objc2-ui-kit/translation-config.toml @@ -21,6 +21,16 @@ static.UIKeyInputF1.skipped = true # These protocol impls would return the wrong types class.NSTextStorage.skipped-protocols = ["NSCopying", "NSMutableCopying"] +# Set counterparts +class.NSParagraphStyle.counterpart = "MutableSubclass(UIKit::NSParagraphStyle::NSMutableParagraphStyle)" +class.NSMutableParagraphStyle.counterpart = "ImmutableSuperclass(UIKit::NSParagraphStyle::NSParagraphStyle)" +class.UIApplicationShortcutItem.counterpart = "MutableSubclass(UIKit::UIApplicationShortcutItem::UIMutableApplicationShortcutItem)" +class.UIMutableApplicationShortcutItem.counterpart = "ImmutableSuperclass(UIKit::UIApplicationShortcutItem::UIApplicationShortcutItem)" +class.UIUserNotificationCategory.counterpart = "MutableSubclass(UIKit::UIUserNotificationSettings::UIMutableUserNotificationCategory)" +class.UIMutableUserNotificationCategory.counterpart = "ImmutableSuperclass(UIKit::UIUserNotificationSettings::UIUserNotificationCategory)" +class.UIUserNotificationAction.counterpart = "MutableSubclass(UIKit::UIUserNotificationSettings::UIMutableUserNotificationAction)" +class.UIMutableUserNotificationAction.counterpart = "ImmutableSuperclass(UIKit::UIUserNotificationSettings::UIUserNotificationAction)" + # These subclass a generic struct, and hence the type parameter defaults to # `AnyObject`, which is not PartialEq, Eq nor Hash. class.NSLayoutXAxisAnchor.derives = "Debug" diff --git a/framework-crates/objc2-user-notifications/translation-config.toml b/framework-crates/objc2-user-notifications/translation-config.toml index 4a7516a3c..127d3d15e 100644 --- a/framework-crates/objc2-user-notifications/translation-config.toml +++ b/framework-crates/objc2-user-notifications/translation-config.toml @@ -7,3 +7,6 @@ ios = "10.0" tvos = "10.0" watchos = "3.0" visionos = "1.0" + +class.UNNotificationContent.counterpart = "MutableSubclass(UserNotifications::UNNotificationContent::UNMutableNotificationContent)" +class.UNMutableNotificationContent.counterpart = "ImmutableSuperclass(UserNotifications::UNNotificationContent::UNNotificationContent)" diff --git a/generated b/generated index 6b9f6ad95..ef8f5f110 160000 --- a/generated +++ b/generated @@ -1 +1 @@ -Subproject commit 6b9f6ad9533d24b62dc1f78c04e5e6f68b57be13 +Subproject commit ef8f5f11053ba020ece249cc3c5c7749b9391086