Improve Objective-C designated initializers detection (#2028)
If Objective-C class doesn't declare a designated initializer, then all of its initializers are considered designated now. See also https://github.com/JetBrains/kotlin-native/issues/1995#issuecomment-418335764
This commit is contained in:
committed by
GitHub
parent
7f00e581b5
commit
55f4c224e9
+1
-1
@@ -842,7 +842,7 @@ internal class NativeIndexImpl(val library: NativeLibrary) : NativeIndex() {
|
|||||||
nsReturnsRetained = hasAttribute(cursor, NS_RETURNS_RETAINED),
|
nsReturnsRetained = hasAttribute(cursor, NS_RETURNS_RETAINED),
|
||||||
isOptional = (clang_Cursor_isObjCOptional(cursor) != 0),
|
isOptional = (clang_Cursor_isObjCOptional(cursor) != 0),
|
||||||
isInit = (clang_Cursor_isObjCInitMethod(cursor) != 0),
|
isInit = (clang_Cursor_isObjCInitMethod(cursor) != 0),
|
||||||
isDesginatedInitializer = hasAttribute(cursor, OBJC_DESGINATED_INITIALIZER)
|
isExplicitlyDesignatedInitializer = hasAttribute(cursor, OBJC_DESGINATED_INITIALIZER)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -143,7 +143,7 @@ sealed class ObjCClassOrProtocol(val name: String) : ObjCContainer(), TypeDeclar
|
|||||||
data class ObjCMethod(
|
data class ObjCMethod(
|
||||||
val selector: String, val encoding: String, val parameters: List<Parameter>, private val returnType: Type,
|
val selector: String, val encoding: String, val parameters: List<Parameter>, private val returnType: Type,
|
||||||
val isClass: Boolean, val nsConsumesSelf: Boolean, val nsReturnsRetained: Boolean,
|
val isClass: Boolean, val nsConsumesSelf: Boolean, val nsReturnsRetained: Boolean,
|
||||||
val isOptional: Boolean, val isInit: Boolean, val isDesginatedInitializer: Boolean
|
val isOptional: Boolean, val isInit: Boolean, val isExplicitlyDesignatedInitializer: Boolean
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun returnsInstancetype(): Boolean = returnType is ObjCInstanceType
|
fun returnsInstancetype(): Boolean = returnType is ObjCInstanceType
|
||||||
|
|||||||
+37
-23
@@ -67,7 +67,8 @@ private fun ObjCMethod.getFirstKotlinParameterNameCandidate(forConstructorOrFact
|
|||||||
|
|
||||||
class ObjCMethodStub(private val stubGenerator: StubGenerator,
|
class ObjCMethodStub(private val stubGenerator: StubGenerator,
|
||||||
val method: ObjCMethod,
|
val method: ObjCMethod,
|
||||||
private val container: ObjCContainer) : KotlinStub, NativeBacked {
|
private val container: ObjCContainer,
|
||||||
|
private val isDesignatedInitializer: Boolean) : KotlinStub, NativeBacked {
|
||||||
|
|
||||||
override fun generate(context: StubGenerationContext): Sequence<String> =
|
override fun generate(context: StubGenerationContext): Sequence<String> =
|
||||||
if (context.nativeBridges.isSupported(this)) {
|
if (context.nativeBridges.isSupported(this)) {
|
||||||
@@ -86,7 +87,7 @@ class ObjCMethodStub(private val stubGenerator: StubGenerator,
|
|||||||
when (container) {
|
when (container) {
|
||||||
is ObjCClass -> {
|
is ObjCClass -> {
|
||||||
// TODO: consider generating non-designated initializers as factories.
|
// TODO: consider generating non-designated initializers as factories.
|
||||||
val designated = method.isDesginatedInitializer ||
|
val designated = isDesignatedInitializer ||
|
||||||
stubGenerator.configuration.disableDesignatedInitializerChecks
|
stubGenerator.configuration.disableDesignatedInitializerChecks
|
||||||
|
|
||||||
result.add("")
|
result.add("")
|
||||||
@@ -364,25 +365,8 @@ private val ObjCClassOrProtocol.superTypes: Sequence<ObjCClassOrProtocol>
|
|||||||
private fun ObjCClassOrProtocol.declaredMethods(isClass: Boolean): Sequence<ObjCMethod> =
|
private fun ObjCClassOrProtocol.declaredMethods(isClass: Boolean): Sequence<ObjCMethod> =
|
||||||
this.methods.asSequence().filter { it.isClass == isClass }
|
this.methods.asSequence().filter { it.isClass == isClass }
|
||||||
|
|
||||||
private fun Sequence<ObjCMethod>.inheritedTo(container: ObjCClassOrProtocol, isMeta: Boolean): Sequence<ObjCMethod> {
|
private fun Sequence<ObjCMethod>.inheritedTo(container: ObjCClassOrProtocol, isMeta: Boolean): Sequence<ObjCMethod> =
|
||||||
// Note: Objective-C initializers act as usual methods and thus are inherited by subclasses.
|
this // TODO: exclude methods that are marked as unavailable in [container].
|
||||||
// Swift considers all super initializers to be available (unless otherwise specified explicitly),
|
|
||||||
// but seems to consider them as non-designated if class declares its own ones.
|
|
||||||
// Simulate the similar behaviour:
|
|
||||||
return if (!isMeta && container.declaredMethods(isMeta).any { it.isDesginatedInitializer }) {
|
|
||||||
this.map {
|
|
||||||
if (it.isDesginatedInitializer) {
|
|
||||||
it.copy(isDesginatedInitializer = false)
|
|
||||||
} else {
|
|
||||||
it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: exclude methods that are marked as unavailable in [container].
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun ObjCClassOrProtocol.inheritedMethods(isClass: Boolean): Sequence<ObjCMethod> =
|
private fun ObjCClassOrProtocol.inheritedMethods(isClass: Boolean): Sequence<ObjCMethod> =
|
||||||
this.immediateSuperTypes.flatMap { it.methodsWithInherited(isClass) }
|
this.immediateSuperTypes.flatMap { it.methodsWithInherited(isClass) }
|
||||||
@@ -392,6 +376,27 @@ private fun ObjCClassOrProtocol.inheritedMethods(isClass: Boolean): Sequence<Obj
|
|||||||
private fun ObjCClassOrProtocol.methodsWithInherited(isClass: Boolean): Sequence<ObjCMethod> =
|
private fun ObjCClassOrProtocol.methodsWithInherited(isClass: Boolean): Sequence<ObjCMethod> =
|
||||||
(this.declaredMethods(isClass) + this.inheritedMethods(isClass)).distinctBy { it.selector }
|
(this.declaredMethods(isClass) + this.inheritedMethods(isClass)).distinctBy { it.selector }
|
||||||
|
|
||||||
|
private fun ObjCClass.getDesignatedInitializerSelectors(result: MutableSet<String>): Set<String> {
|
||||||
|
// Note: Objective-C initializers act as usual methods and thus are inherited by subclasses.
|
||||||
|
// Swift considers all super initializers to be available (unless otherwise specified explicitly),
|
||||||
|
// but seems to consider them as non-designated if class declares its own ones explicitly.
|
||||||
|
// Simulate the similar behaviour:
|
||||||
|
val explicitlyDesignatedInitializers = this.methods.filter { it.isExplicitlyDesignatedInitializer && !it.isClass }
|
||||||
|
|
||||||
|
if (explicitlyDesignatedInitializers.isNotEmpty()) {
|
||||||
|
explicitlyDesignatedInitializers.mapTo(result) { it.selector }
|
||||||
|
} else {
|
||||||
|
this.declaredMethods(isClass = false).filter { it.isInit }.mapTo(result) { it.selector }
|
||||||
|
this.baseClass?.getDesignatedInitializerSelectors(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.superTypes.filterIsInstance<ObjCProtocol>()
|
||||||
|
.flatMap { it.declaredMethods(isClass = false) }.filter { it.isInit }
|
||||||
|
.mapTo(result) { it.selector }
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
private fun ObjCMethod.isOverride(container: ObjCClassOrProtocol): Boolean =
|
private fun ObjCMethod.isOverride(container: ObjCClassOrProtocol): Boolean =
|
||||||
container.superTypes.any { superType -> superType.methods.any(this::replaces) }
|
container.superTypes.any { superType -> superType.methods.any(this::replaces) }
|
||||||
|
|
||||||
@@ -446,8 +451,17 @@ abstract class ObjCContainerStub(stubGenerator: StubGenerator,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val designatedInitializerSelectors = if (container is ObjCClass && !isMeta) {
|
||||||
|
container.getDesignatedInitializerSelectors(mutableSetOf())
|
||||||
|
} else {
|
||||||
|
emptySet()
|
||||||
|
}
|
||||||
|
|
||||||
private val methodToStub = methods.map {
|
private val methodToStub = methods.map {
|
||||||
it to ObjCMethodStub(stubGenerator, it, container)
|
it to ObjCMethodStub(
|
||||||
|
stubGenerator, it, container,
|
||||||
|
isDesignatedInitializer = it.selector in designatedInitializerSelectors
|
||||||
|
)
|
||||||
}.toMap()
|
}.toMap()
|
||||||
|
|
||||||
private val methodStubs get() = methodToStub.values
|
private val methodStubs get() = methodToStub.values
|
||||||
@@ -568,7 +582,7 @@ class ObjCCategoryStub(
|
|||||||
// TODO: consider removing members that are also present in the class or its supertypes.
|
// TODO: consider removing members that are also present in the class or its supertypes.
|
||||||
|
|
||||||
private val methodToStub = category.methods.map {
|
private val methodToStub = category.methods.map {
|
||||||
it to ObjCMethodStub(stubGenerator, it, category)
|
it to ObjCMethodStub(stubGenerator, it, category, isDesignatedInitializer = false)
|
||||||
}.toMap()
|
}.toMap()
|
||||||
|
|
||||||
private val methodStubs get() = methodToStub.values
|
private val methodStubs get() = methodToStub.values
|
||||||
|
|||||||
Reference in New Issue
Block a user