[ObjCExport] Support naming of nested classes
^KT-65204 Fixed
This commit is contained in:
committed by
Space Team
parent
f1fd84f539
commit
4b6624c920
+95
-2
@@ -6,16 +6,109 @@
|
||||
package org.jetbrains.kotlin.objcexport
|
||||
|
||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtClassKind
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtClassLikeSymbol
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.nameOrAnonymous
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportClassOrProtocolName
|
||||
import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeAsciiOnly
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
fun KtClassLikeSymbol.getObjCClassOrProtocolName(): ObjCExportClassOrProtocolName {
|
||||
val resolvedObjCNameAnnotation = resolveObjCNameAnnotation()
|
||||
|
||||
return ObjCExportClassOrProtocolName(
|
||||
objCName = resolvedObjCNameAnnotation?.objCName ?: nameOrAnonymous.asString(),
|
||||
swiftName = resolvedObjCNameAnnotation?.swiftName ?: nameOrAnonymous.asString()
|
||||
objCName = getObjCName(resolvedObjCNameAnnotation),
|
||||
swiftName = getSwiftName(resolvedObjCNameAnnotation)
|
||||
)
|
||||
}
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
private fun KtClassLikeSymbol.getObjCName(
|
||||
resolvedObjCNameAnnotation: KtResolvedObjCNameAnnotation? = resolveObjCNameAnnotation(),
|
||||
): String {
|
||||
val objCName = (resolvedObjCNameAnnotation?.objCName ?: nameOrAnonymous.asString()).toValidObjCSwiftIdentifier()
|
||||
|
||||
if (resolvedObjCNameAnnotation != null && resolvedObjCNameAnnotation.isExact) {
|
||||
return objCName
|
||||
}
|
||||
|
||||
getContainingSymbol()?.let { it as? KtClassLikeSymbol }?.let { containingClass ->
|
||||
return containingClass.getObjCName() + objCName.capitalizeAsciiOnly()
|
||||
}
|
||||
|
||||
// KT-65670: Append module specific prefixes?
|
||||
return configuration.frameworkName.orEmpty() + objCName
|
||||
}
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
private fun KtClassLikeSymbol.getSwiftName(
|
||||
resolvedObjCNameAnnotation: KtResolvedObjCNameAnnotation? = resolveObjCNameAnnotation(),
|
||||
): String {
|
||||
val swiftName = (resolvedObjCNameAnnotation?.swiftName ?: nameOrAnonymous.asString()).toValidObjCSwiftIdentifier()
|
||||
if (resolvedObjCNameAnnotation != null && resolvedObjCNameAnnotation.isExact) {
|
||||
return swiftName
|
||||
}
|
||||
|
||||
getContainingSymbol()?.let { it as? KtClassLikeSymbol }?.let { containingClass ->
|
||||
val containingClassSwiftName = containingClass.getSwiftName()
|
||||
return buildString {
|
||||
if (canBeInnerSwift()) {
|
||||
append(containingClassSwiftName)
|
||||
if ("." !in this && containingClass.canBeOuterSwift()) {
|
||||
// AB -> AB.C
|
||||
append(".")
|
||||
append(mangleSwiftNestedClassName(swiftName))
|
||||
} else {
|
||||
// AB -> ABC
|
||||
// A.B -> A.BC
|
||||
append(swiftName.capitalizeAsciiOnly())
|
||||
}
|
||||
} else {
|
||||
append(containingClassSwiftName.replaceFirst(".", ""))
|
||||
append(swiftName.capitalizeAsciiOnly())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// KT-65670: Append module specific prefixes?
|
||||
return swiftName
|
||||
|
||||
}
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
private fun KtClassLikeSymbol.canBeInnerSwift(): Boolean {
|
||||
if (configuration.objcGenerics && this.typeParameters.isNotEmpty()) {
|
||||
// Swift compiler doesn't seem to handle this case properly.
|
||||
// See https://bugs.swift.org/browse/SR-14607.
|
||||
// This behaviour of Kotlin is reported as https://youtrack.jetbrains.com/issue/KT-46518.
|
||||
return false
|
||||
}
|
||||
|
||||
if (this is KtClassOrObjectSymbol && this.classKind == KtClassKind.INTERFACE) {
|
||||
// Swift doesn't support nested protocols.
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
private fun KtClassLikeSymbol.canBeOuterSwift(): Boolean {
|
||||
if (configuration.objcGenerics && this.typeParameters.isNotEmpty()) {
|
||||
// Swift nested classes are static but capture outer's generics.
|
||||
return false
|
||||
}
|
||||
|
||||
if (this is KtClassOrObjectSymbol && this.classKind == KtClassKind.INTERFACE) {
|
||||
// Swift doesn't support outer protocols.
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun mangleSwiftNestedClassName(name: String): String = when (name) {
|
||||
"Type" -> "${name}_" // See https://github.com/JetBrains/kotlin-native/issues/3167
|
||||
else -> name
|
||||
}
|
||||
+2
-1
@@ -17,9 +17,10 @@ fun KtClassOrObjectSymbol.translateToObjCClass(): ObjCClass? {
|
||||
|
||||
val enumKind = this.classKind == KtClassKind.ENUM_CLASS
|
||||
val final = if (this is KtSymbolWithModality) this.modality == Modality.FINAL else false
|
||||
val attributes = if (enumKind || final) listOf(OBJC_SUBCLASSING_RESTRICTED) else emptyList()
|
||||
|
||||
val name = getObjCClassOrProtocolName()
|
||||
val attributes = (if (enumKind || final) listOf(OBJC_SUBCLASSING_RESTRICTED) else emptyList()) + name.toNameAttributes()
|
||||
|
||||
val comment: ObjCComment? = annotationsList.translateToObjCComment()
|
||||
val origin: ObjCExportStubOrigin = getObjCExportStubOrigin()
|
||||
|
||||
|
||||
+15
-5
@@ -70,15 +70,26 @@ class ObjCExportHeaderGeneratorTest(private val generator: HeaderGenerator) {
|
||||
doTest(headersTestDataDir.resolve("sameClassNameInDifferentPackage"))
|
||||
}
|
||||
|
||||
/**
|
||||
* Naming of nested classes: KT-65633
|
||||
*/
|
||||
@Test
|
||||
@TodoAnalysisApi
|
||||
fun `test - nestedClass`() {
|
||||
doTest(headersTestDataDir.resolve("nestedClass"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - nestedClassWithFrameworkName`() {
|
||||
doTest(headersTestDataDir.resolve("nestedClassWithFrameworkName"), Configuration(frameworkName = "Shared"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - nestedGenericClass`() {
|
||||
doTest(headersTestDataDir.resolve("nestedGenericClass"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - nestedInterface`() {
|
||||
doTest(headersTestDataDir.resolve("nestedInterface"))
|
||||
}
|
||||
|
||||
@Test
|
||||
@TodoAnalysisApi
|
||||
fun `test - samePropertyAndFunctionName`() {
|
||||
@@ -97,7 +108,6 @@ class ObjCExportHeaderGeneratorTest(private val generator: HeaderGenerator) {
|
||||
}
|
||||
|
||||
@Test
|
||||
@TodoAnalysisApi
|
||||
fun `test - classWithObjCNameAnnotation`() {
|
||||
doTest(headersTestDataDir.resolve("classWithObjCNameAnnotation"))
|
||||
}
|
||||
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSError.h>
|
||||
#import <Foundation/NSObject.h>
|
||||
#import <Foundation/NSSet.h>
|
||||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/NSValue.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunknown-warning-option"
|
||||
#pragma clang diagnostic ignored "-Wincompatible-property-type"
|
||||
#pragma clang diagnostic ignored "-Wnullability"
|
||||
|
||||
#pragma push_macro("_Nullable_result")
|
||||
#if !__has_feature(nullability_nullable_result)
|
||||
#undef _Nullable_result
|
||||
#define _Nullable_result _Nullable
|
||||
#endif
|
||||
|
||||
__attribute__((objc_subclassing_restricted))
|
||||
__attribute__((swift_name("A")))
|
||||
@interface SharedA : SharedBase
|
||||
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
|
||||
+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead")));
|
||||
@end
|
||||
|
||||
__attribute__((objc_subclassing_restricted))
|
||||
__attribute__((swift_name("A.A1")))
|
||||
@interface SharedAA1 : SharedBase
|
||||
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
|
||||
+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead")));
|
||||
@end
|
||||
|
||||
__attribute__((objc_subclassing_restricted))
|
||||
__attribute__((swift_name("A.A1B1")))
|
||||
@interface SharedAA1B1 : SharedBase
|
||||
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
|
||||
+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead")));
|
||||
@end
|
||||
|
||||
__attribute__((objc_subclassing_restricted))
|
||||
__attribute__((swift_name("A.A2")))
|
||||
@interface SharedAA2 : SharedBase
|
||||
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
|
||||
+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead")));
|
||||
@end
|
||||
|
||||
__attribute__((objc_subclassing_restricted))
|
||||
__attribute__((swift_name("A.A2B2")))
|
||||
@interface SharedAA2B2 : SharedBase
|
||||
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
|
||||
+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead")));
|
||||
@end
|
||||
|
||||
#pragma pop_macro("_Nullable_result")
|
||||
#pragma clang diagnostic pop
|
||||
NS_ASSUME_NONNULL_END
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
class A {
|
||||
class A1 {
|
||||
class B1
|
||||
}
|
||||
class A2 {
|
||||
class B2
|
||||
}
|
||||
}
|
||||
Vendored
+41
@@ -0,0 +1,41 @@
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSError.h>
|
||||
#import <Foundation/NSObject.h>
|
||||
#import <Foundation/NSSet.h>
|
||||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/NSValue.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunknown-warning-option"
|
||||
#pragma clang diagnostic ignored "-Wincompatible-property-type"
|
||||
#pragma clang diagnostic ignored "-Wnullability"
|
||||
|
||||
#pragma push_macro("_Nullable_result")
|
||||
#if !__has_feature(nullability_nullable_result)
|
||||
#undef _Nullable_result
|
||||
#define _Nullable_result _Nullable
|
||||
#endif
|
||||
|
||||
__attribute__((objc_subclassing_restricted))
|
||||
@interface A : Base
|
||||
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
|
||||
+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead")));
|
||||
@end
|
||||
|
||||
__attribute__((objc_subclassing_restricted))
|
||||
@interface AB<T> : Base
|
||||
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
|
||||
+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead")));
|
||||
@end
|
||||
|
||||
__attribute__((objc_subclassing_restricted))
|
||||
@interface ABC<T> : Base
|
||||
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
|
||||
+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead")));
|
||||
@end
|
||||
|
||||
#pragma pop_macro("_Nullable_result")
|
||||
#pragma clang diagnostic pop
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,5 @@
|
||||
class A {
|
||||
class B<T> {
|
||||
class C<T>
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSError.h>
|
||||
#import <Foundation/NSObject.h>
|
||||
#import <Foundation/NSSet.h>
|
||||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/NSValue.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunknown-warning-option"
|
||||
#pragma clang diagnostic ignored "-Wincompatible-property-type"
|
||||
#pragma clang diagnostic ignored "-Wnullability"
|
||||
|
||||
#pragma push_macro("_Nullable_result")
|
||||
#if !__has_feature(nullability_nullable_result)
|
||||
#undef _Nullable_result
|
||||
#define _Nullable_result _Nullable
|
||||
#endif
|
||||
|
||||
@protocol A
|
||||
@required
|
||||
@end
|
||||
|
||||
@protocol AB
|
||||
@required
|
||||
@end
|
||||
|
||||
@protocol ABC
|
||||
@required
|
||||
@end
|
||||
|
||||
#pragma pop_macro("_Nullable_result")
|
||||
#pragma clang diagnostic pop
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,5 @@
|
||||
interface A {
|
||||
interface B {
|
||||
interface C
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user