[ObjCExport] Implement initial processing queue (stable order, dependencies, forward declarations)

This commit will introduce the first processing queue
which will take care of properly ordering the 'to translate' symbols
as well as taking care of 'dependency' symbols (aka symbols
mentioned in signatures or as supertypes).

- There are several changes like properly translating
types as ObjCProtocolType instead of ObjCClassType (if origin was an interface)
- Type translation of generics defined on interfaces will also emit id type.
- Add initial nested classes collection to the queue
- Add extension function test, add tickets references

^KT-65237 Verification Pending
^KT-65329 Verification Pending
This commit is contained in:
eugene.levenetc
2024-01-26 12:46:39 +01:00
committed by Space Team
parent 32da1b70f5
commit 797284dada
40 changed files with 1213 additions and 117 deletions
@@ -0,0 +1,20 @@
# Translation Order
This document will compile all knowledge about the order of translation of symbols and in which order those symbols shall
be present in the resulting header.
## Rule 1
We first order symbols by package (alphabetically), then by containing file name, then by class name.
Note:
Classifiers order will not be further documented here as it is not contextual (at least as we understand it right now)
## Rule 2
If a type is mentioned as supertype, we see that the supertype is declared in the resulting header *before* the using class;
however, the supertype naming will be ordered *after* the current class
## Rule 3
If a type is mentioned as return or paramter type, we see a forward declaration being emitted, but the resulting order
of the header is unaffected.
@@ -44,5 +44,5 @@ internal val errorInterface
superClassGenerics = emptyList() superClassGenerics = emptyList()
) )
internal val objCErrorType = ObjCClassType(errorClassName) internal val objCErrorType = ObjCClassType(errorClassName, classId = null)
internal val errorForwardClass = ObjCClassForwardDeclaration(errorClassName) internal val errorForwardClass = ObjCClassForwardDeclaration(errorClassName)
@@ -0,0 +1,40 @@
package org.jetbrains.kotlin.objcexport.analysisApiUtils
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtClassifierSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtFileSymbol
/**
* Returns all classes or objects (including transitively nested ones) contained within a file
*
* Example:
*```kotlin
* class A {
* class B {
* class C
* }
* }
*```
* returns `sequenceOf(A, B, C)`
*/
context(KtAnalysisSession)
internal fun KtFileSymbol.getAllClassOrObjectSymbols(): List<KtClassifierSymbol> {
return getFileScope().getClassifierSymbols()
.filterIsInstance<KtClassOrObjectSymbol>()
.flatMap { classSymbol -> listOf(classSymbol) + classSymbol.getAllClassOrObjectSymbols() }
.toList()
}
context(KtAnalysisSession)
private fun KtClassOrObjectSymbol.getAllClassOrObjectSymbols(): Sequence<KtClassOrObjectSymbol> {
return sequence {
val nestedClasses = getMemberScope().getClassifierSymbols().filterIsInstance<KtClassOrObjectSymbol>()
yieldAll(nestedClasses)
nestedClasses.forEach { nestedClass ->
yieldAll(nestedClass.getAllClassOrObjectSymbols())
}
}
}
@@ -0,0 +1,28 @@
/*
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.objcexport.analysisApiUtils
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.symbols.KtClassKind
import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol
import org.jetbrains.kotlin.analysis.api.types.KtClassType
import org.jetbrains.kotlin.analysis.api.types.KtClassTypeQualifier
/**
* @return The **declared** super interfaces (**not the transitive closure**)
*/
context(KtAnalysisSession)
internal fun KtClassOrObjectSymbol.getDeclaredSuperInterfaceSymbols(): List<KtClassOrObjectSymbol> {
return superTypes
.asSequence()
.mapNotNull { type -> type as? KtClassType }
.flatMap { type -> type.qualifiers }
.mapNotNull { qualifier -> qualifier as? KtClassTypeQualifier.KtResolvedClassTypeQualifier }
.mapNotNull { it.symbol as? KtClassOrObjectSymbol }
.filter { !it.isCloneable } // TODO: Write unit test for this
.filter { superInterface -> superInterface.classKind == KtClassKind.INTERFACE }
.toList()
}
@@ -0,0 +1,15 @@
package org.jetbrains.kotlin.objcexport.analysisApiUtils
import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.name.ClassId
internal val KtClassOrObjectSymbol.isCloneable: Boolean
get() {
return classIdIfNonLocal?.isCloneable ?: false
}
internal val ClassId.isCloneable: Boolean
get() {
return asSingleFqName() == StandardNames.FqNames.cloneable.toSafe()
}
@@ -0,0 +1,18 @@
/*
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.objcexport
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCProtocol
import org.jetbrains.kotlin.objcexport.analysisApiUtils.getDeclaredSuperInterfaceSymbols
context(KtAnalysisSession, KtObjCExportSession)
internal fun KtClassOrObjectSymbol.translateSuperInterfaces(): List<ObjCProtocol> {
return getDeclaredSuperInterfaceSymbols().mapNotNull { superInterfaceSymbol ->
superInterfaceSymbol.translateToObjCProtocol()
}
}
@@ -6,7 +6,6 @@ import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithModality import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithModality
import org.jetbrains.kotlin.backend.konan.objcexport.* import org.jetbrains.kotlin.backend.konan.objcexport.*
import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.objcexport.analysisApiUtils.getAllMembers
import org.jetbrains.kotlin.objcexport.analysisApiUtils.getDefaultSuperClassOrProtocolName import org.jetbrains.kotlin.objcexport.analysisApiUtils.getDefaultSuperClassOrProtocolName
import org.jetbrains.kotlin.objcexport.analysisApiUtils.getSuperClassSymbolNotAny import org.jetbrains.kotlin.objcexport.analysisApiUtils.getSuperClassSymbolNotAny
import org.jetbrains.kotlin.objcexport.analysisApiUtils.isVisibleInObjC import org.jetbrains.kotlin.objcexport.analysisApiUtils.isVisibleInObjC
@@ -18,7 +17,7 @@ fun KtClassOrObjectSymbol.translateToObjCClass(): ObjCClass? {
val superClass = getSuperClassSymbolNotAny() val superClass = getSuperClassSymbolNotAny()
val kotlinAnyName = getDefaultSuperClassOrProtocolName() val kotlinAnyName = getDefaultSuperClassOrProtocolName()
val superName = if (superClass == null) kotlinAnyName else throw RuntimeException("Super class translation isn't implemented yet") val superName = if (superClass == null) kotlinAnyName else getSuperClassName()
val enumKind = this.classKind == KtClassKind.ENUM_CLASS val enumKind = this.classKind == KtClassKind.ENUM_CLASS
val final = if (this is KtSymbolWithModality) this.modality == Modality.FINAL else false val final = if (this is KtSymbolWithModality) this.modality == Modality.FINAL else false
val attributes = if (enumKind || final) listOf(OBJC_SUBCLASSING_RESTRICTED) else emptyList() val attributes = if (enumKind || final) listOf(OBJC_SUBCLASSING_RESTRICTED) else emptyList()
@@ -28,9 +27,10 @@ fun KtClassOrObjectSymbol.translateToObjCClass(): ObjCClass? {
val origin: ObjCExportStubOrigin = getObjCExportStubOrigin() val origin: ObjCExportStubOrigin = getObjCExportStubOrigin()
val superProtocols: List<String> = superProtocols() val superProtocols: List<String> = superProtocols()
val members: List<ObjCExportStub> = getAllMembers() val members: List<ObjCExportStub> = getMemberScope().getCallableSymbols().plus(getMemberScope().getConstructors())
.sortedWith(StableSymbolOrder) .sortedWith(StableCallableOrder)
.flatMap { it.translateToObjCExportStubs() } .flatMap { it.translateToObjCExportStubs() }
.toList()
val categoryName: String? = null val categoryName: String? = null
val generics: List<ObjCGenericTypeDeclaration> = emptyList() val generics: List<ObjCGenericTypeDeclaration> = emptyList()
@@ -57,6 +57,13 @@ private fun abbreviate(name: String): String {
val uppers = normalizedName.filterIndexed { index, character -> index == 0 || character.isUpperCase() } val uppers = normalizedName.filterIndexed { index, character -> index == 0 || character.isUpperCase() }
if (uppers.length >= 3) return uppers if (uppers.length >= 3) return uppers
return normalizedName return normalizedName
} }
/**
* See issue KT-65384
* And K1 implementation [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportTranslatorImpl.translateClass]
*/
private fun KtClassOrObjectSymbol.getSuperClassName(): ObjCExportClassOrProtocolName {
return ObjCExportClassOrProtocolName("UnimplementedSwiftName", "UnimplementedObjCName")
}
@@ -0,0 +1,18 @@
package org.jetbrains.kotlin.objcexport
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.symbols.KtCallableSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtConstructorSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtFunctionSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtPropertySymbol
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportStub
context(KtAnalysisSession, KtObjCExportSession)
internal fun KtCallableSymbol.translateToObjCExportStubs(): List<ObjCExportStub> {
return when (this) {
is KtConstructorSymbol -> translateToObjCConstructors()
is KtPropertySymbol -> listOfNotNull(translateToObjCProperty())
is KtFunctionSymbol -> listOfNotNull(translateToObjCMethod())
else -> emptyList()
}
}
@@ -1,94 +1,136 @@
/* /*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. * Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
package org.jetbrains.kotlin.objcexport package org.jetbrains.kotlin.objcexport
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.symbols.* import org.jetbrains.kotlin.analysis.api.symbols.KtClassKind
import org.jetbrains.kotlin.analysis.api.symbols.KtClassKind.* import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtFileSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol
import org.jetbrains.kotlin.backend.konan.objcexport.* import org.jetbrains.kotlin.backend.konan.objcexport.*
import org.jetbrains.kotlin.objcexport.analysisApiUtils.errorForwardClass import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.objcexport.analysisApiUtils.errorInterface import org.jetbrains.kotlin.objcexport.analysisApiUtils.*
import org.jetbrains.kotlin.objcexport.analysisApiUtils.hasErrorTypes
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.utils.addIfNotNull
context(KtAnalysisSession, KtObjCExportSession) context(KtAnalysisSession, KtObjCExportSession)
fun translateToObjCHeader(files: List<KtFile>): ObjCHeader { fun translateToObjCHeader(files: List<KtFile>): ObjCHeader {
val declarations = files val stubs = mutableListOf<ObjCExportStub>()
.sortedWith(StableFileOrder) val protocolForwardDeclarations = mutableSetOf<String>()
.flatMap { file -> file.getFileSymbol().translateToObjCExportStubs() } val classForwardDeclarations = mutableSetOf<ObjCClassForwardDeclaration>()
.toMutableList()
val classForwardDeclarations = getClassForwardDeclarations(declarations).toMutableSet() val symbolTranslationQueue = mutableListOf<KtSymbol>()
val protocolForwardDeclarations = getProtocolForwardDeclarations(declarations) val translatedClassifiers = mutableMapOf<ClassId, ObjCClass>()
if (declarations.hasErrorTypes()) { fun process(
declarations.add(errorInterface) symbol: KtSymbol,
forwardProtocolsAndClasses: Boolean = false,
): List<ObjCExportStub> {
val result = mutableListOf<ObjCExportStub>()
when (symbol) {
/* In this case: Go and translate all classes/objects/interfaces inside the file as well */
is KtFileSymbol -> {
val translatedTopLevelFileFacade = symbol.translateToObjCTopLevelInterfaceFileFacade()
result.addIfNotNull(translatedTopLevelFileFacade)
symbolTranslationQueue.addAll(
symbol.getAllClassOrObjectSymbols().sortedWith(StableClassifierOrder)
)
}
/* Translate the Class/Interface, but ensure that all supertypes will be translated also */
is KtClassOrObjectSymbol -> {
val classId = symbol.classIdIfNonLocal ?: return result
/* Add the classId to already processed classIds and do not redo if already processed */
val stub = translatedClassifiers.getOrPut(classId) {
val translatedObjCClassOrProtocol = when (symbol.classKind) {
KtClassKind.INTERFACE -> symbol.translateToObjCProtocol()
KtClassKind.CLASS -> symbol.translateToObjCClass()
KtClassKind.OBJECT -> symbol.translateToObjCObject()
else -> return result
} ?: return result
symbol.getDeclaredSuperInterfaceSymbols().forEach { superInterfaceSymbol ->
result.addAll(process(superInterfaceSymbol))
}
result.add(translatedObjCClassOrProtocol)
translatedObjCClassOrProtocol
}
if (forwardProtocolsAndClasses) {
when (stub) {
is ObjCInterface -> classForwardDeclarations.add(ObjCClassForwardDeclaration(stub.name, stub.generics))
is ObjCProtocol -> protocolForwardDeclarations.add(stub.name)
}
}
}
}
return result
}
fun processDeclaredSymbols(symbols: List<KtSymbol>): List<ObjCExportStub> {
val result = mutableListOf<ObjCExportStub>()
symbolTranslationQueue.addAll(symbols)
while (true) {
val next = symbolTranslationQueue.removeFirstOrNull() ?: break
result.addAll(process(next))
}
return result
}
fun processDependencySymbols(stubs: List<ObjCExportStub>): List<ObjCExportStub> {
val dependencyClassSymbols = stubs.closureSequence()
.mapNotNull { stub ->
when (stub) {
is ObjCMethod -> stub.returnType
is ObjCParameter -> stub.type
is ObjCProperty -> stub.type
is ObjCTopLevel -> null
}
}
.flatMap { type ->
if (type is ObjCClassType) type.typeArguments + type
else listOf(type)
}
.mapNotNull { if (it is ObjCReferenceType) it.classId else null }
.mapNotNull { classId -> getClassOrObjectSymbolByClassId(classId) }
.toList()
symbolTranslationQueue.addAll(dependencyClassSymbols)
val result = dependencyClassSymbols.flatMap { symbol ->
process(symbol, forwardProtocolsAndClasses = true)
}
return if (result.isNotEmpty()) result + processDependencySymbols(result)
else result
}
val fileSymbols = files.sortedWith(StableFileOrder).map { it.getFileSymbol() }
val declaredStubs = processDeclaredSymbols(fileSymbols)
val dependencyStubs = processDependencySymbols(declaredStubs)
stubs.addAll(declaredStubs + dependencyStubs)
if (stubs.hasErrorTypes()) {
stubs.add(errorInterface)
classForwardDeclarations.add(errorForwardClass) classForwardDeclarations.add(errorForwardClass)
} }
protocolForwardDeclarations += stubs
.filterIsInstance<ObjCClass>()
.flatMap { it.superProtocols }
return ObjCHeader( return ObjCHeader(
stubs = declarations, stubs = stubs,
classForwardDeclarations = classForwardDeclarations, classForwardDeclarations = classForwardDeclarations,
protocolForwardDeclarations = protocolForwardDeclarations, protocolForwardDeclarations = protocolForwardDeclarations,
additionalImports = emptyList(), additionalImports = emptyList()
) )
}
/**
* Class which have static property must have forward declaration
*
* ```
* @class Foo;
*
* @interface Foo
* @property (class) Foo
* @end
* ```
*/
private fun getClassForwardDeclarations(declarations: List<ObjCExportStub>): Set<ObjCClassForwardDeclaration> {
return declarations
.filterIsInstance<ObjCClass>()
.filter { clazz ->
clazz.members
.filterIsInstance<ObjCProperty>()
.any { property ->
val className = (property.type as? ObjCClassType)?.className == clazz.name
val static = property.propertyAttributes.contains("class")
className && static
}
}.map { clazz ->
ObjCClassForwardDeclaration(clazz.name)
}.toSet()
}
private fun getProtocolForwardDeclarations(declarations: List<ObjCExportStub>) = declarations
.filterIsInstance<ObjCClass>()
.flatMap { it.superProtocols }
.toSet()
context(KtAnalysisSession, KtObjCExportSession)
fun KtFileSymbol.translateToObjCExportStubs(): List<ObjCExportStub> {
return listOfNotNull(translateToObjCTopLevelInterfaceFileFacade()) + getFileScope().getClassifierSymbols()
.sortedWith(StableClassifierOrder)
.flatMap { classifierSymbol -> classifierSymbol.translateToObjCExportStubs() }
}
context(KtAnalysisSession, KtObjCExportSession)
internal fun KtSymbol.translateToObjCExportStubs(): List<ObjCExportStub> {
return when {
this is KtFileSymbol -> translateToObjCExportStubs()
this is KtClassOrObjectSymbol && classKind == INTERFACE -> listOfNotNull(translateToObjCProtocol())
this is KtClassOrObjectSymbol && classKind == CLASS -> listOfNotNull(translateToObjCClass())
this is KtClassOrObjectSymbol && classKind == OBJECT -> listOfNotNull(translateToObjCObject())
this is KtConstructorSymbol -> translateToObjCConstructors()
this is KtPropertySymbol -> listOfNotNull(translateToObjCProperty())
this is KtFunctionSymbol -> listOfNotNull(translateToObjCMethod())
else -> emptyList()
}
} }
@@ -6,7 +6,6 @@ import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithModality import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithModality
import org.jetbrains.kotlin.backend.konan.objcexport.* import org.jetbrains.kotlin.backend.konan.objcexport.*
import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.objcexport.analysisApiUtils.getAllMembers
import org.jetbrains.kotlin.objcexport.analysisApiUtils.getDefaultSuperClassOrProtocolName import org.jetbrains.kotlin.objcexport.analysisApiUtils.getDefaultSuperClassOrProtocolName
import org.jetbrains.kotlin.objcexport.analysisApiUtils.getSuperClassSymbolNotAny import org.jetbrains.kotlin.objcexport.analysisApiUtils.getSuperClassSymbolNotAny
import org.jetbrains.kotlin.objcexport.analysisApiUtils.isVisibleInObjC import org.jetbrains.kotlin.objcexport.analysisApiUtils.isVisibleInObjC
@@ -32,9 +31,10 @@ fun KtClassOrObjectSymbol.translateToObjCObject(): ObjCClass? {
val superClassGenerics: List<ObjCNonNullReferenceType> = emptyList() val superClassGenerics: List<ObjCNonNullReferenceType> = emptyList()
val objectMembers = getDefaultMembers() val objectMembers = getDefaultMembers()
getAllMembers().flatMap { it.translateToObjCExportStubs() }.forEach { getMemberScope().getCallableSymbols()
objectMembers.add(it) .sortedWith(StableCallableOrder)
} .flatMap { it.translateToObjCExportStubs() }
.forEach { objectMembers.add(it) }
return ObjCInterfaceImpl( return ObjCInterfaceImpl(
name.objCName, name.objCName,
@@ -97,7 +97,8 @@ private fun KtClassOrObjectSymbol.getDefaultMembers(): MutableList<ObjCExportStu
*/ */
private fun KtClassOrObjectSymbol.toPropertyType() = ObjCClassType( private fun KtClassOrObjectSymbol.toPropertyType() = ObjCClassType(
this.classIdIfNonLocal!!.shortClassName.asString(), this.classIdIfNonLocal!!.shortClassName.asString(),
emptyList() emptyList(),
classIdIfNonLocal!!
) )
/** /**
@@ -8,13 +8,12 @@ package org.jetbrains.kotlin.objcexport
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.symbols.KtClassKind import org.jetbrains.kotlin.analysis.api.symbols.KtClassKind
import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol
import org.jetbrains.kotlin.analysis.api.types.KtClassType
import org.jetbrains.kotlin.analysis.api.types.KtClassTypeQualifier
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCComment import org.jetbrains.kotlin.backend.konan.objcexport.ObjCComment
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCProtocol import org.jetbrains.kotlin.backend.konan.objcexport.ObjCProtocol
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCProtocolImpl import org.jetbrains.kotlin.backend.konan.objcexport.ObjCProtocolImpl
import org.jetbrains.kotlin.backend.konan.objcexport.toNameAttributes import org.jetbrains.kotlin.backend.konan.objcexport.toNameAttributes
import org.jetbrains.kotlin.objcexport.analysisApiUtils.getDeclaredMembers import org.jetbrains.kotlin.objcexport.analysisApiUtils.getDeclaredSuperInterfaceSymbols
import org.jetbrains.kotlin.objcexport.analysisApiUtils.isCloneable
import org.jetbrains.kotlin.objcexport.analysisApiUtils.isVisibleInObjC import org.jetbrains.kotlin.objcexport.analysisApiUtils.isVisibleInObjC
context(KtAnalysisSession, KtObjCExportSession) context(KtAnalysisSession, KtObjCExportSession)
@@ -26,9 +25,10 @@ fun KtClassOrObjectSymbol.translateToObjCProtocol(): ObjCProtocol? {
// TODO: Check error type! // TODO: Check error type!
val name = getObjCClassOrProtocolName() val name = getObjCClassOrProtocolName()
val members = getDeclaredMembers() val members = getMemberScope().getCallableSymbols()
.sortedWith(StableSymbolOrder) .sortedWith(StableCallableOrder)
.flatMap { it.translateToObjCExportStubs() } .flatMap { it.translateToObjCExportStubs() }
.toList()
val comment: ObjCComment? = annotationsList.translateToObjCComment() val comment: ObjCComment? = annotationsList.translateToObjCComment()
@@ -44,14 +44,8 @@ fun KtClassOrObjectSymbol.translateToObjCProtocol(): ObjCProtocol? {
context(KtAnalysisSession, KtObjCExportSession) context(KtAnalysisSession, KtObjCExportSession)
internal fun KtClassOrObjectSymbol.superProtocols(): List<String> { internal fun KtClassOrObjectSymbol.superProtocols(): List<String> {
return superTypes return getDeclaredSuperInterfaceSymbols()
.asSequence() .filter { superInterface -> !superInterface.isCloneable }
.filter { type -> !type.isAny }
.mapNotNull { type -> type as? KtClassType }
.flatMap { type -> type.qualifiers }
.mapNotNull { qualifier -> qualifier as? KtClassTypeQualifier.KtResolvedClassTypeQualifier }
.mapNotNull { it.symbol as? KtClassOrObjectSymbol }
.filter { superInterface -> superInterface.classKind == KtClassKind.INTERFACE }
.map { superInterface -> superInterface.getObjCClassOrProtocolName().objCName } .map { superInterface -> superInterface.getObjCClassOrProtocolName().objCName }
.toList() .toList()
} }
@@ -4,6 +4,8 @@ import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.KtStarTypeProjection import org.jetbrains.kotlin.analysis.api.KtStarTypeProjection
import org.jetbrains.kotlin.analysis.api.KtTypeArgumentWithVariance import org.jetbrains.kotlin.analysis.api.KtTypeArgumentWithVariance
import org.jetbrains.kotlin.analysis.api.symbols.KtCallableSymbol import org.jetbrains.kotlin.analysis.api.symbols.KtCallableSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtClassKind
import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtNamedClassOrObjectSymbol import org.jetbrains.kotlin.analysis.api.symbols.KtNamedClassOrObjectSymbol
import org.jetbrains.kotlin.analysis.api.types.KtErrorType import org.jetbrains.kotlin.analysis.api.types.KtErrorType
import org.jetbrains.kotlin.analysis.api.types.KtNonErrorClassType import org.jetbrains.kotlin.analysis.api.types.KtNonErrorClassType
@@ -79,9 +81,9 @@ private fun KtType.mapToReferenceTypeIgnoringNullability(): ObjCNonNullReference
} }
/* Check if inline type represents 'regular' inline class */ /* Check if inline type represents 'regular' inline class */
val classSymbol: KtClassOrObjectSymbol? = if (classId != null) getClassOrObjectSymbolByClassId(classId) else null
run check@{ run check@{
if (classId == null) return@check if (classId == null) return@check
val classSymbol = getClassOrObjectSymbolByClassId(classId) ?: return@check
if (classSymbol !is KtNamedClassOrObjectSymbol) return@check if (classSymbol !is KtNamedClassOrObjectSymbol) return@check
if (classSymbol.isInline) return ObjCIdType if (classSymbol.isInline) return ObjCIdType
} }
@@ -112,15 +114,30 @@ private fun KtType.mapToReferenceTypeIgnoringNullability(): ObjCNonNullReference
} }
if (fullyExpandedType is KtNonErrorClassType) { if (fullyExpandedType is KtNonErrorClassType) {
val typeName = typesMap[classId] typesMap[classId]?.let { typeName ->
?: fullyExpandedType.classId.shortClassName.asString().getObjCKotlinStdlibClassOrProtocolName().objCName val typeArguments = translateToObjCTypeArguments()
return ObjCClassType(typeName, typeArguments, classId = null) // todo: not clear for reader why null is important here!
}
val typeName = fullyExpandedType.classId.shortClassName.asString().getObjCKotlinStdlibClassOrProtocolName().objCName
val typeArguments = translateToObjCTypeArguments() val typeArguments = translateToObjCTypeArguments()
return ObjCClassType(typeName, typeArguments)
// TODO NOW: create type translation test
if (classSymbol?.classKind == KtClassKind.INTERFACE) {
return ObjCProtocolType(typeName, classId)
}
return ObjCClassType(typeName, typeArguments, classId)
} }
if (fullyExpandedType is KtTypeParameterType) { if (fullyExpandedType is KtTypeParameterType) {
if (fullyExpandedType.symbol.getContainingSymbol() is KtCallableSymbol) { val definingSymbol = fullyExpandedType.symbol.getContainingSymbol()
if (definingSymbol is KtCallableSymbol) {
return ObjCIdType
}
if (definingSymbol is KtClassOrObjectSymbol && definingSymbol.classKind == KtClassKind.INTERFACE) {
return ObjCIdType return ObjCIdType
} }
/* /*
@@ -0,0 +1,89 @@
package org.jetbrains.kotlin.objcexport.tests
import org.intellij.lang.annotations.Language
import org.jetbrains.kotlin.analysis.api.analyze
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCHeader
import org.jetbrains.kotlin.objcexport.KtObjCExportConfiguration
import org.jetbrains.kotlin.objcexport.KtObjCExportSession
import org.jetbrains.kotlin.objcexport.testUtils.InlineSourceCodeAnalysis
import org.jetbrains.kotlin.objcexport.translateToObjCHeader
import org.jetbrains.kotlin.psi.KtFile
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals
/**
* Tests logic of filtering protocols and classes which must be forwarded or excluded when dependency is used
* For full tests of dependencies see [ObjCDependenciesTypesTest]
*/
class ForwardedClassesAndProtocolsDependenciesTest(
private val inlineSourceCodeAnalysis: InlineSourceCodeAnalysis,
) {
@Test
fun `test - iterator`() {
doTest(
code = """
val i: Iterator<Int>
""",
protocols = setOf("Iterator"),
classes = emptySet()
)
}
@Test
fun `test - array`() {
doTest(
code = """
val i: Array<Int>
""",
protocols = setOf("Iterator"),
classes = setOf("Array")
)
}
@Test
fun `test - string builder`() {
doTest(
code = """
val i: StringBuilder
""",
protocols = setOf("CharSequence", "Appendable", "Iterator"),
classes = setOf("StringBuilder", "CharArray", "CharIterator")
)
}
@Test
fun `test - declared symbols`() {
doTest(
code = """
open class ClassA
class ClassB: ClassA()
interface InterfaceA
interface InterfaceB : InterfaceA()
fun getClass(): ClassB = error("error")
fun getInterface(): InterfaceB = error("error")
""",
protocols = setOf("InterfaceB", "InterfaceA"),
classes = setOf("ClassB")
)
}
private fun doTest(
@Language("kotlin") code: String,
protocols: Set<String>,
classes: Set<String>,
) {
val file = inlineSourceCodeAnalysis.createKtFile(code.trimIndent())
val classesAndProtocols = translateClassesAndProtocols(file)
assertEquals(protocols, classesAndProtocols.protocolForwardDeclarations.toSet(), "Invalid protocols set")
assertEquals(classes, classesAndProtocols.classForwardDeclarations.map { it.className }.toSet(), "Invalid classes set")
}
private fun translateClassesAndProtocols(file: KtFile): ObjCHeader {
return analyze(file) {
KtObjCExportSession(KtObjCExportConfiguration()) {
translateToObjCHeader(listOf(file))
}
}
}
}
@@ -0,0 +1,65 @@
/*
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.objcexport.tests
import org.jetbrains.kotlin.analysis.api.analyze
import org.jetbrains.kotlin.objcexport.analysisApiUtils.getAllClassOrObjectSymbols
import org.jetbrains.kotlin.objcexport.testUtils.InlineSourceCodeAnalysis
import org.jetbrains.kotlin.objcexport.testUtils.getClassOrFail
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals
class GetAllClassOrObjectSymbolsTest(
private val inlineSourceCodeAnalysis: InlineSourceCodeAnalysis,
) {
@Test
fun `test - no classifiers in file`() {
val file = inlineSourceCodeAnalysis.createKtFile("val foo = 42")
analyze(file) {
assertEquals(emptyList(), file.getFileSymbol().getAllClassOrObjectSymbols())
}
}
@Test
fun `test - single class in file`() {
val file = inlineSourceCodeAnalysis.createKtFile("class Foo")
analyze(file) {
assertEquals(listOf(file.getClassOrFail("Foo")), file.getFileSymbol().getAllClassOrObjectSymbols())
}
}
@Test
fun `test - multiple nested classes in file`() {
val file = inlineSourceCodeAnalysis.createKtFile(
"""
class A {
class B {
class C
}
}
class D {
class E
}
""".trimIndent()
)
analyze(file) {
assertEquals(
listOf(
file.getClassOrFail("A"),
file.getClassOrFail("A").getMemberScope().getClassOrFail("B"),
file.getClassOrFail("A").getMemberScope().getClassOrFail("B").getMemberScope().getClassOrFail("C"),
file.getClassOrFail("D"),
file.getClassOrFail("D").getMemberScope().getClassOrFail("E")
),
file.getFileSymbol().getAllClassOrObjectSymbols()
)
}
}
}
@@ -0,0 +1,79 @@
/*
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.objcexport.tests
import org.jetbrains.kotlin.analysis.api.analyze
import org.jetbrains.kotlin.objcexport.analysisApiUtils.getDeclaredSuperInterfaceSymbols
import org.jetbrains.kotlin.objcexport.testUtils.InlineSourceCodeAnalysis
import org.jetbrains.kotlin.objcexport.testUtils.getClassOrFail
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals
class GetSuperInterfacesTest(
private val inlineSourceCodeAnalysis: InlineSourceCodeAnalysis,
) {
@Test
fun `test - transitive super interfaces`() {
val file = inlineSourceCodeAnalysis.createKtFile(
"""
interface A
interface B
interface C
interface X: A, B
interface Y : C
class Foo: X, Y
""".trimIndent()
)
analyze(file) {
val foo = file.getClassOrFail("Foo")
assertEquals(
listOf(file.getClassOrFail("X"), file.getClassOrFail("Y")),
foo.getDeclaredSuperInterfaceSymbols()
)
}
}
@Test
fun `test - super interface and super class`() {
val file = inlineSourceCodeAnalysis.createKtFile(
"""
interface A
interface B
abstract class X
class Foo: X(), A, B
""".trimIndent()
)
analyze(file) {
assertEquals(
listOf(file.getClassOrFail("A"), file.getClassOrFail("B")),
file.getClassOrFail("Foo").getDeclaredSuperInterfaceSymbols()
)
}
}
@Test
fun `test - subclassing Any explicitly`() {
val file = inlineSourceCodeAnalysis.createKtFile(
"""
interface A
interface B
class Foo: Any(), A, B
""".trimIndent()
)
analyze(file) {
assertEquals(
listOf(file.getClassOrFail("A"), file.getClassOrFail("B")),
file.getClassOrFail("Foo").getDeclaredSuperInterfaceSymbols()
)
}
}
}
@@ -0,0 +1,46 @@
package org.jetbrains.kotlin.objcexport.tests
import junit.framework.TestCase.assertFalse
import junit.framework.TestCase.assertTrue
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.analyze
import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtPropertySymbol
import org.jetbrains.kotlin.objcexport.analysisApiUtils.isCloneable
import org.jetbrains.kotlin.objcexport.testUtils.InlineSourceCodeAnalysis
import org.jetbrains.kotlin.objcexport.testUtils.getPropertyOrFail
import org.junit.jupiter.api.Test
class IsCloneableTest(
private val inlineSourceCodeAnalysis: InlineSourceCodeAnalysis,
) {
@Test
fun `test - is cloneable`() {
val file = inlineSourceCodeAnalysis.createKtFile("val foo: Cloneable")
analyze(file) {
val foo = file.getPropertyOrFail("foo")
assertTrue(foo.type.isCloneable)
}
}
@Test
fun `test - is iterator cloneable`() {
val file = inlineSourceCodeAnalysis.createKtFile(
"""
class A
val foo: A
""".trimIndent()
)
analyze(file) {
val foo = file.getPropertyOrFail("foo")
assertFalse(foo.type.isCloneable)
}
}
}
context(KtAnalysisSession)
private val KtPropertySymbol.type: KtClassOrObjectSymbol
get() {
return getter?.returnType?.expandedClassSymbol!!
}
@@ -197,7 +197,8 @@ abstract class ObjCExportHeaderGenerator @InternalKotlinNativeApi constructor(
private fun generateInterface(descriptor: ClassDescriptor) { private fun generateInterface(descriptor: ClassDescriptor) {
if (!generatedClasses.add(descriptor)) return if (!generatedClasses.add(descriptor)) return
stubs.add(translator.translateInterface(descriptor)) val stub = translator.translateInterface(descriptor)
stubs.add(stub)
} }
internal fun requireClassOrInterface(descriptor: ClassDescriptor) { internal fun requireClassOrInterface(descriptor: ClassDescriptor) {
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.backend.konan.objcexport package org.jetbrains.kotlin.backend.konan.objcexport
import org.jetbrains.kotlin.backend.konan.InternalKotlinNativeApi import org.jetbrains.kotlin.backend.konan.InternalKotlinNativeApi
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.types.Variance
sealed class ObjCType { sealed class ObjCType {
@@ -25,7 +26,10 @@ data class ObjCRawType(
override fun render(attrsAndName: String): String = rawText.withAttrsAndName(attrsAndName) override fun render(attrsAndName: String): String = rawText.withAttrsAndName(attrsAndName)
} }
sealed class ObjCReferenceType : ObjCType() sealed class ObjCReferenceType : ObjCType() {
@InternalKotlinNativeApi
open val classId: ClassId? = null
}
sealed class ObjCNonNullReferenceType : ObjCReferenceType() sealed class ObjCNonNullReferenceType : ObjCReferenceType()
@@ -33,6 +37,9 @@ data class ObjCNullableReferenceType(
val nonNullType: ObjCNonNullReferenceType, val nonNullType: ObjCNonNullReferenceType,
val isNullableResult: Boolean = false, val isNullableResult: Boolean = false,
) : ObjCReferenceType() { ) : ObjCReferenceType() {
override val classId: ClassId? get() = nonNullType.classId
override fun render(attrsAndName: String): String { override fun render(attrsAndName: String): String {
val attribute = if (isNullableResult) objcNullableResultAttribute else objcNullableAttribute val attribute = if (isNullableResult) objcNullableResultAttribute else objcNullableAttribute
return nonNullType.render(" $attribute".withAttrsAndName(attrsAndName)) return nonNullType.render(" $attribute".withAttrsAndName(attrsAndName))
@@ -42,8 +49,10 @@ data class ObjCNullableReferenceType(
data class ObjCClassType( data class ObjCClassType(
val className: String, val className: String,
val typeArguments: List<ObjCNonNullReferenceType> = emptyList(), val typeArguments: List<ObjCNonNullReferenceType> = emptyList(),
override val classId: ClassId? = null,
) : ObjCNonNullReferenceType() { ) : ObjCNonNullReferenceType() {
override fun render(attrsAndName: String) = buildString { override fun render(attrsAndName: String) = buildString {
append(className) append(className)
if (typeArguments.isNotEmpty()) { if (typeArguments.isNotEmpty()) {
@@ -66,11 +75,12 @@ sealed class ObjCGenericTypeUsage : ObjCNonNullReferenceType() {
data class ObjCGenericTypeRawUsage(override val typeName: String) : ObjCGenericTypeUsage() data class ObjCGenericTypeRawUsage(override val typeName: String) : ObjCGenericTypeUsage()
data class ObjCGenericTypeParameterUsage( data class ObjCGenericTypeParameterUsage(
override val typeName: String override val typeName: String,
) : ObjCGenericTypeUsage() ) : ObjCGenericTypeUsage()
data class ObjCProtocolType( data class ObjCProtocolType(
val protocolName: String, val protocolName: String,
override val classId: ClassId? = null,
) : ObjCNonNullReferenceType() { ) : ObjCNonNullReferenceType() {
override fun render(attrsAndName: String) = "id<$protocolName>".withAttrsAndName(attrsAndName) override fun render(attrsAndName: String) = "id<$protocolName>".withAttrsAndName(attrsAndName)
} }
@@ -87,7 +97,6 @@ data class ObjCBlockPointerType(
val returnType: ObjCType, val returnType: ObjCType,
val parameterTypes: List<ObjCReferenceType>, val parameterTypes: List<ObjCReferenceType>,
) : ObjCNonNullReferenceType() { ) : ObjCNonNullReferenceType() {
override fun render(attrsAndName: String) = returnType.render(buildString { override fun render(attrsAndName: String) = returnType.render(buildString {
append("(^") append("(^")
append(attrsAndName) append(attrsAndName)
@@ -199,7 +208,7 @@ data class ObjCGenericTypeRawDeclaration(
data class ObjCGenericTypeParameterDeclaration( data class ObjCGenericTypeParameterDeclaration(
override val typeName: String, override val typeName: String,
override val variance: ObjCVariance override val variance: ObjCVariance,
) : ObjCGenericTypeDeclaration() ) : ObjCGenericTypeDeclaration()
@InternalKotlinNativeApi @InternalKotlinNativeApi
@@ -10,4 +10,5 @@ import java.io.File
val testDataDir = File("native/objcexport-header-generator/testData") val testDataDir = File("native/objcexport-header-generator/testData")
val headersTestDataDir = testDataDir.resolve("headers") val headersTestDataDir = testDataDir.resolve("headers")
val baseDeclarationsDir = testDataDir.resolve("baseDeclarations") val baseDeclarationsDir = testDataDir.resolve("baseDeclarations")
val forwardDeclarationsDir = testDataDir.resolve("forwardDeclarations") val forwardDeclarationsDir = testDataDir.resolve("forwardDeclarations")
val dependenciesDir = testDataDir.resolve("dependencies")
@@ -0,0 +1,59 @@
/*
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.konan.tests
import org.jetbrains.kotlin.backend.konan.testUtils.HeaderGenerator
import org.jetbrains.kotlin.backend.konan.testUtils.TodoAnalysisApi
import org.jetbrains.kotlin.backend.konan.testUtils.dependenciesDir
import org.jetbrains.kotlin.test.KotlinTestUtils
import org.junit.jupiter.api.Test
import java.io.File
import kotlin.test.fail
/**
* Currently analysis api doesn't fails until full support of stable order and other parts
*
* Test intended to verify how dependencies are translated, which included
* - stable orders
* - stubs orders
* - depth of traversing (some types must be skipped
*/
class ObjCDependenciesTypesTest(
private val generator: HeaderGenerator,
) {
/**
* - Wrong translation of constructors KT-65365
* - Bad parsing of versions (SinceKotlin Annotation)
* - Missing implementation of mangling
*/
@Test
@TodoAnalysisApi
fun `test - stringBuilder`() {
doTest(dependenciesDir.resolve("stringBuilder"))
}
@Test
fun `test - iterator`() {
doTest(dependenciesDir.resolve("iterator"))
}
/**
* - Wrong translation of constructors KT-65365
* - Exposing unwanted 'clone' method KT-65629
*/
@Test
@TodoAnalysisApi
fun `test - array`() {
doTest(dependenciesDir.resolve("array"))
}
private fun doTest(root: File) {
if (!root.isDirectory) fail("Expected ${root.absolutePath} to be directory")
val generatedHeaders = generator.generateHeaders(root, HeaderGenerator.Configuration()).toString()
KotlinTestUtils.assertEqualsToFile(root.resolve("!${root.nameWithoutExtension}.h"), generatedHeaders)
}
}
@@ -6,7 +6,6 @@
package org.jetbrains.kotlin.backend.konan.tests package org.jetbrains.kotlin.backend.konan.tests
import org.jetbrains.kotlin.backend.konan.testUtils.HeaderGenerator import org.jetbrains.kotlin.backend.konan.testUtils.HeaderGenerator
import org.jetbrains.kotlin.backend.konan.testUtils.TodoAnalysisApi
import org.jetbrains.kotlin.backend.konan.testUtils.forwardDeclarationsDir import org.jetbrains.kotlin.backend.konan.testUtils.forwardDeclarationsDir
import org.jetbrains.kotlin.test.KotlinTestUtils import org.jetbrains.kotlin.test.KotlinTestUtils
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
@@ -18,25 +17,21 @@ class ObjCExportForwardDeclarationsTest(
) { ) {
@Test @Test
@TodoAnalysisApi
fun `test - function returning interface`() { fun `test - function returning interface`() {
doTest(forwardDeclarationsDir.resolve("functionReturningInterface")) doTest(forwardDeclarationsDir.resolve("functionReturningInterface"))
} }
@Test @Test
@TodoAnalysisApi
fun `test - function returning class`() { fun `test - function returning class`() {
doTest(forwardDeclarationsDir.resolve("functionReturningClass")) doTest(forwardDeclarationsDir.resolve("functionReturningClass"))
} }
@Test @Test
@TodoAnalysisApi
fun `test - property returning interface`() { fun `test - property returning interface`() {
doTest(forwardDeclarationsDir.resolve("propertyReturningInterface")) doTest(forwardDeclarationsDir.resolve("propertyReturningInterface"))
} }
@Test @Test
@TodoAnalysisApi
fun `test - property returning class`() { fun `test - property returning class`() {
doTest(forwardDeclarationsDir.resolve("propertyReturningClass")) doTest(forwardDeclarationsDir.resolve("propertyReturningClass"))
} }
@@ -70,6 +70,9 @@ class ObjCExportHeaderGeneratorTest(private val generator: HeaderGenerator) {
doTest(headersTestDataDir.resolve("sameClassNameInDifferentPackage")) doTest(headersTestDataDir.resolve("sameClassNameInDifferentPackage"))
} }
/**
* Naming of nested classes: KT-65633
*/
@Test @Test
@TodoAnalysisApi @TodoAnalysisApi
fun `test - nestedClass`() { fun `test - nestedClass`() {
@@ -83,7 +86,6 @@ class ObjCExportHeaderGeneratorTest(private val generator: HeaderGenerator) {
} }
@Test @Test
@TodoAnalysisApi // Early translate of forward declarations
fun `test - classImplementingInterface`() { fun `test - classImplementingInterface`() {
doTest(headersTestDataDir.resolve("classImplementingInterface")) doTest(headersTestDataDir.resolve("classImplementingInterface"))
} }
@@ -178,7 +180,6 @@ class ObjCExportHeaderGeneratorTest(private val generator: HeaderGenerator) {
} }
@Test @Test
@TodoAnalysisApi // Early translate of forward declarations
fun `test - classWithUnresolvedSuperTypeGenerics`() { fun `test - classWithUnresolvedSuperTypeGenerics`() {
doTest(headersTestDataDir.resolve("classWithUnresolvedSuperTypeGenerics")) doTest(headersTestDataDir.resolve("classWithUnresolvedSuperTypeGenerics"))
} }
@@ -198,6 +199,40 @@ class ObjCExportHeaderGeneratorTest(private val generator: HeaderGenerator) {
doTest(headersTestDataDir.resolve("manyClassesAndInterfaces")) doTest(headersTestDataDir.resolve("manyClassesAndInterfaces"))
} }
@Test
fun `test - classReferencingOtherClassAsReturnType`() {
doTest(headersTestDataDir.resolve("classReferencingOtherClassAsReturnType"))
}
/**
* - IntIterator has unwanted 'hasNext' exposed
* - IntIterator's next method returns int32_t instead of expected Int *
*/
@Test
@TodoAnalysisApi
fun `test - classReferencingDependencyClassAsReturnType`() {
doTest(headersTestDataDir.resolve("classReferencingDependencyClassAsReturnType"))
}
@Test
fun `test - interfaceReferencingOtherInterfaceAsReturnType`() {
doTest(headersTestDataDir.resolve("interfaceReferencingOtherInterfaceAsReturnType"))
}
@Test
fun `test - interfaceImplementingInterfaceOrder`() {
doTest(headersTestDataDir.resolve("interfaceImplementingInterfaceOrder"))
}
/**
* Extension functions aren't supported KT-65630
*/
@Test
@TodoAnalysisApi
fun `test - extensionFunctions`() {
doTest(headersTestDataDir.resolve("extensionFunctions"))
}
private fun doTest(root: File, configuration: Configuration = Configuration()) { private fun doTest(root: File, configuration: Configuration = Configuration()) {
if (!root.isDirectory) fail("Expected ${root.absolutePath} to be directory") if (!root.isDirectory) fail("Expected ${root.absolutePath} to be directory")
val generatedHeaders = generator.generateHeaders(root, configuration).toString() val generatedHeaders = generator.generateHeaders(root, configuration).toString()
@@ -0,0 +1,49 @@
#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>
@class Array<T>;
@protocol Iterator;
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 FooKt : Base
@property (class, readonly) Array<Int *> *a __attribute__((swift_name("a")));
@end
__attribute__((objc_subclassing_restricted))
@interface Array<T> : Base
+ (instancetype)arrayWithSize:(int32_t)size init:(T _Nullable (^)(Int *))init __attribute__((swift_name("init(size:init:)")));
+ (instancetype)alloc __attribute__((unavailable));
+ (instancetype)allocWithZone:(struct _NSZone *)zone __attribute__((unavailable));
- (T _Nullable)getIndex:(int32_t)index __attribute__((swift_name("get(index:)")));
- (id<Iterator>)iterator __attribute__((swift_name("iterator()")));
- (void)setIndex:(int32_t)index value:(T _Nullable)value __attribute__((swift_name("set(index:value:)")));
@property (readonly) int32_t size __attribute__((swift_name("size")));
@end
@protocol Iterator
@required
- (BOOL)hasNext __attribute__((swift_name("hasNext()")));
- (id _Nullable)next __attribute__((swift_name("next()")));
@end
#pragma pop_macro("_Nullable_result")
#pragma clang diagnostic pop
NS_ASSUME_NONNULL_END
@@ -0,0 +1 @@
val a: Array<Int>
@@ -0,0 +1,36 @@
#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>
@protocol Iterator;
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 FooKt : Base
@property (class, readonly) id<Iterator> a __attribute__((swift_name("a")));
@end
@protocol Iterator
@required
- (BOOL)hasNext __attribute__((swift_name("hasNext()")));
- (id _Nullable)next __attribute__((swift_name("next()")));
@end
#pragma pop_macro("_Nullable_result")
#pragma clang diagnostic pop
NS_ASSUME_NONNULL_END
@@ -0,0 +1 @@
val a: Iterator<Int>
@@ -0,0 +1,194 @@
#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>
@class StringBuilder, CharArray, CharIterator;
@protocol CharSequence, Appendable, Iterator;
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 FooKt : Base
@property (class, readonly) StringBuilder *a __attribute__((swift_name("a")));
@end
@protocol CharSequence
@required
- (unichar)getIndex:(int32_t)index __attribute__((swift_name("get(index:)")));
- (id)subSequenceStartIndex:(int32_t)startIndex endIndex:(int32_t)endIndex __attribute__((swift_name("subSequence(startIndex:endIndex:)")));
@property (readonly) int32_t length __attribute__((swift_name("length")));
@end
@protocol Appendable
@required
- (id<Appendable>)appendValue:(unichar)value __attribute__((swift_name("append(value:)")));
- (id<Appendable>)appendValue_:(id _Nullable)value __attribute__((swift_name("append(value_:)")));
- (id<Appendable>)appendValue:(id _Nullable)value startIndex:(int32_t)startIndex endIndex:(int32_t)endIndex __attribute__((swift_name("append(value:startIndex:endIndex:)")));
@end
__attribute__((objc_subclassing_restricted))
@interface StringBuilder : Base <CharSequence, Appendable>
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead")));
- (instancetype)initWithContent:(id)content __attribute__((swift_name("init(content:)"))) __attribute__((objc_designated_initializer));
- (instancetype)initWithCapacity:(int32_t)capacity __attribute__((swift_name("init(capacity:)"))) __attribute__((objc_designated_initializer));
- (instancetype)initWithContent_:(NSString *)content __attribute__((swift_name("init(content_:)"))) __attribute__((objc_designated_initializer));
- (StringBuilder *)appendValue__:(id _Nullable)value __attribute__((swift_name("append(value__:)")));
- (StringBuilder *)appendValue___:(BOOL)value __attribute__((swift_name("append(value___:)")));
- (StringBuilder *)appendValue____:(int8_t)value __attribute__((swift_name("append(value____:)")));
- (StringBuilder *)appendValue:(unichar)value __attribute__((swift_name("append(value:)")));
- (StringBuilder *)appendValue_____:(CharArray *)value __attribute__((swift_name("append(value_____:)")));
- (StringBuilder *)appendValue_:(id _Nullable)value __attribute__((swift_name("append(value_:)")));
- (StringBuilder *)appendValue______:(double)value __attribute__((swift_name("append(value______:)")));
- (StringBuilder *)appendValue_______:(float)value __attribute__((swift_name("append(value_______:)")));
- (StringBuilder *)appendValue________:(int32_t)value __attribute__((swift_name("append(value________:)")));
- (StringBuilder *)appendValue_________:(int64_t)value __attribute__((swift_name("append(value_________:)")));
- (StringBuilder *)appendValue__________:(int16_t)value __attribute__((swift_name("append(value__________:)")));
- (StringBuilder *)appendValue___________:(NSString * _Nullable)value __attribute__((swift_name("append(value___________:)")));
- (StringBuilder *)appendValue:(id _Nullable)value startIndex:(int32_t)startIndex endIndex:(int32_t)endIndex __attribute__((swift_name("append(value:startIndex:endIndex:)")));
/**
* @note annotations
* kotlin.SinceKotlin(version="1.4")
*/
- (StringBuilder *)appendRangeValue:(CharArray *)value startIndex:(int32_t)startIndex endIndex:(int32_t)endIndex __attribute__((swift_name("appendRange(value:startIndex:endIndex:)")));
/**
* @note annotations
* kotlin.SinceKotlin(version="1.4")
*/
- (StringBuilder *)appendRangeValue:(id)value startIndex:(int32_t)startIndex endIndex_:(int32_t)endIndex __attribute__((swift_name("appendRange(value:startIndex:endIndex_:)")));
- (int32_t)capacity __attribute__((swift_name("capacity()")));
/**
* @note annotations
* kotlin.SinceKotlin(version="1.4")
*/
- (StringBuilder *)deleteAtIndex:(int32_t)index __attribute__((swift_name("deleteAt(index:)")));
/**
* @note annotations
* kotlin.SinceKotlin(version="1.4")
*/
- (StringBuilder *)deleteRangeStartIndex:(int32_t)startIndex endIndex:(int32_t)endIndex __attribute__((swift_name("deleteRange(startIndex:endIndex:)")));
- (void)ensureCapacityMinimumCapacity:(int32_t)minimumCapacity __attribute__((swift_name("ensureCapacity(minimumCapacity:)")));
- (unichar)getIndex:(int32_t)index __attribute__((swift_name("get(index:)")));
/**
* @note annotations
* kotlin.SinceKotlin(version="1.4")
*/
- (int32_t)indexOfString:(NSString *)string __attribute__((swift_name("indexOf(string:)")));
/**
* @note annotations
* kotlin.SinceKotlin(version="1.4")
*/
- (int32_t)indexOfString:(NSString *)string startIndex:(int32_t)startIndex __attribute__((swift_name("indexOf(string:startIndex:)")));
- (StringBuilder *)insertIndex:(int32_t)index value:(id _Nullable)value __attribute__((swift_name("insert(index:value:)")));
- (StringBuilder *)insertIndex:(int32_t)index value_:(BOOL)value __attribute__((swift_name("insert(index:value_:)")));
- (StringBuilder *)insertIndex:(int32_t)index value__:(int8_t)value __attribute__((swift_name("insert(index:value__:)")));
- (StringBuilder *)insertIndex:(int32_t)index value___:(unichar)value __attribute__((swift_name("insert(index:value___:)")));
- (StringBuilder *)insertIndex:(int32_t)index value____:(CharArray *)value __attribute__((swift_name("insert(index:value____:)")));
- (StringBuilder *)insertIndex:(int32_t)index value_____:(id _Nullable)value __attribute__((swift_name("insert(index:value_____:)")));
- (StringBuilder *)insertIndex:(int32_t)index value______:(double)value __attribute__((swift_name("insert(index:value______:)")));
- (StringBuilder *)insertIndex:(int32_t)index value_______:(float)value __attribute__((swift_name("insert(index:value_______:)")));
- (StringBuilder *)insertIndex:(int32_t)index value________:(int32_t)value __attribute__((swift_name("insert(index:value________:)")));
- (StringBuilder *)insertIndex:(int32_t)index value_________:(int64_t)value __attribute__((swift_name("insert(index:value_________:)")));
- (StringBuilder *)insertIndex:(int32_t)index value__________:(int16_t)value __attribute__((swift_name("insert(index:value__________:)")));
- (StringBuilder *)insertIndex:(int32_t)index value___________:(NSString * _Nullable)value __attribute__((swift_name("insert(index:value___________:)")));
/**
* @note annotations
* kotlin.SinceKotlin(version="1.4")
*/
- (StringBuilder *)insertRangeIndex:(int32_t)index value:(CharArray *)value startIndex:(int32_t)startIndex endIndex:(int32_t)endIndex __attribute__((swift_name("insertRange(index:value:startIndex:endIndex:)")));
/**
* @note annotations
* kotlin.SinceKotlin(version="1.4")
*/
- (StringBuilder *)insertRangeIndex:(int32_t)index value:(id)value startIndex:(int32_t)startIndex endIndex_:(int32_t)endIndex __attribute__((swift_name("insertRange(index:value:startIndex:endIndex_:)")));
/**
* @note annotations
* kotlin.SinceKotlin(version="1.4")
*/
- (int32_t)lastIndexOfString:(NSString *)string __attribute__((swift_name("lastIndexOf(string:)")));
/**
* @note annotations
* kotlin.SinceKotlin(version="1.4")
*/
- (int32_t)lastIndexOfString:(NSString *)string startIndex:(int32_t)startIndex __attribute__((swift_name("lastIndexOf(string:startIndex:)")));
- (StringBuilder *)reverse __attribute__((swift_name("reverse()")));
- (void)setIndex:(int32_t)index value:(unichar)value __attribute__((swift_name("set(index:value:)")));
- (void)setLengthNewLength:(int32_t)newLength __attribute__((swift_name("setLength(newLength:)")));
/**
* @note annotations
* kotlin.SinceKotlin(version="1.4")
*/
- (StringBuilder *)setRangeStartIndex:(int32_t)startIndex endIndex:(int32_t)endIndex value:(NSString *)value __attribute__((swift_name("setRange(startIndex:endIndex:value:)")));
- (id)subSequenceStartIndex:(int32_t)startIndex endIndex:(int32_t)endIndex __attribute__((swift_name("subSequence(startIndex:endIndex:)")));
/**
* @note annotations
* kotlin.SinceKotlin(version="1.4")
*/
- (NSString *)substringStartIndex:(int32_t)startIndex __attribute__((swift_name("substring(startIndex:)")));
- (NSString *)substringStartIndex:(int32_t)startIndex endIndex:(int32_t)endIndex __attribute__((swift_name("substring(startIndex:endIndex:)")));
/**
* @note annotations
* kotlin.SinceKotlin(version="1.4")
*/
- (void)toCharArrayDestination:(CharArray *)destination destinationOffset:(int32_t)destinationOffset startIndex:(int32_t)startIndex endIndex:(int32_t)endIndex __attribute__((swift_name("toCharArray(destination:destinationOffset:startIndex:endIndex:)")));
- (NSString *)description __attribute__((swift_name("description()")));
- (void)trimToSize __attribute__((swift_name("trimToSize()")));
@property (readonly) int32_t length __attribute__((swift_name("length")));
@end
__attribute__((objc_subclassing_restricted))
@interface CharArray : Base
+ (instancetype)arrayWithSize:(int32_t)size __attribute__((swift_name("init(size:)")));
+ (instancetype)arrayWithSize:(int32_t)size init:(id _Nullable (^)(id _Nullable))init __attribute__((swift_name("init(size:init:)")));
+ (instancetype)alloc __attribute__((unavailable));
+ (instancetype)allocWithZone:(struct _NSZone *)zone __attribute__((unavailable));
- (unichar)getIndex:(int32_t)index __attribute__((swift_name("get(index:)")));
- (CharIterator *)iterator __attribute__((swift_name("iterator()")));
- (void)setIndex:(int32_t)index value:(unichar)value __attribute__((swift_name("set(index:value:)")));
@property (readonly) int32_t size __attribute__((swift_name("size")));
@end
@protocol Iterator
@required
- (BOOL)hasNext __attribute__((swift_name("hasNext()")));
- (id _Nullable)next __attribute__((swift_name("next()")));
@end
@interface CharIterator : Base <Iterator>
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead")));
- (id)next __attribute__((swift_name("next()")));
- (unichar)nextChar __attribute__((swift_name("nextChar()")));
@end
#pragma pop_macro("_Nullable_result")
#pragma clang diagnostic pop
NS_ASSUME_NONNULL_END
@@ -0,0 +1 @@
val a: StringBuilder
@@ -0,0 +1,47 @@
#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>
@class IntIterator;
@protocol Iterator;
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 Foo : Base
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead")));
- (IntIterator *)foo __attribute__((swift_name("foo()")));
@end
@protocol Iterator
@required
- (BOOL)hasNext __attribute__((swift_name("hasNext()")));
- (id _Nullable)next __attribute__((swift_name("next()")));
@end
@interface IntIterator : Base <Iterator>
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead")));
- (Int *)next __attribute__((swift_name("next()")));
- (int32_t)nextInt __attribute__((swift_name("nextInt()")));
@end
#pragma pop_macro("_Nullable_result")
#pragma clang diagnostic pop
NS_ASSUME_NONNULL_END
@@ -0,0 +1,3 @@
class Foo {
fun foo(): IntIterator = error("stub")
}
@@ -0,0 +1,38 @@
#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>
@class B;
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")));
- (B *)foo __attribute__((swift_name("foo()")));
@end
__attribute__((objc_subclassing_restricted))
@interface B : 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 {
fun foo(): B = B()
}
class B
@@ -0,0 +1,36 @@
#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>
@class Foo;
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 Foo : Base
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead")));
- (void)funA __attribute__((swift_name("funA()")));
@end
@interface Foo (Extensions)
- (void)funB __attribute__((swift_name("funB()")));
@end
#pragma pop_macro("_Nullable_result")
#pragma clang diagnostic pop
NS_ASSUME_NONNULL_END
@@ -0,0 +1,5 @@
class Foo {
fun funA() {}
}
fun Foo.funB() {}
@@ -0,0 +1,33 @@
#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>
@protocol B;
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 B
@required
@end
@protocol A <B>
@required
@end
#pragma pop_macro("_Nullable_result")
#pragma clang diagnostic pop
NS_ASSUME_NONNULL_END
@@ -0,0 +1,3 @@
interface A : B
interface B
@@ -0,0 +1,34 @@
#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>
@protocol B;
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
- (id<B>)foo __attribute__((swift_name("foo()")));
@end
@protocol B
@required
@end
#pragma pop_macro("_Nullable_result")
#pragma clang diagnostic pop
NS_ASSUME_NONNULL_END
@@ -0,0 +1,5 @@
interface A {
fun foo(): B
}
interface B
@@ -25,8 +25,29 @@ __attribute__((objc_subclassing_restricted))
@end @end
__attribute__((objc_subclassing_restricted)) __attribute__((objc_subclassing_restricted))
__attribute__((swift_name("A.B"))) __attribute__((swift_name("A.A1")))
@interface AB : Base @interface AA1 : 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))
__attribute__((swift_name("A.A1B1")))
@interface AA1B1 : 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))
__attribute__((swift_name("A.A2")))
@interface AA2 : 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))
__attribute__((swift_name("A.A2B2")))
@interface AA2B2 : Base
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); - (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); + (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead")));
@end @end
@@ -1,3 +1,8 @@
class A { class A {
class B class A1 {
class B1
}
class A2 {
class B2
}
} }