diff --git a/native/objcexport-header-generator/TranslationOrder.md b/native/objcexport-header-generator/TranslationOrder.md new file mode 100644 index 00000000000..372dcf0228d --- /dev/null +++ b/native/objcexport-header-generator/TranslationOrder.md @@ -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. + + diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/analysisApiUtils/errors.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/analysisApiUtils/errors.kt index c1f94957c19..69788cfb896 100644 --- a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/analysisApiUtils/errors.kt +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/analysisApiUtils/errors.kt @@ -44,5 +44,5 @@ internal val errorInterface superClassGenerics = emptyList() ) -internal val objCErrorType = ObjCClassType(errorClassName) +internal val objCErrorType = ObjCClassType(errorClassName, classId = null) internal val errorForwardClass = ObjCClassForwardDeclaration(errorClassName) \ No newline at end of file diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/analysisApiUtils/getAllClassOrObjectSymbols.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/analysisApiUtils/getAllClassOrObjectSymbols.kt new file mode 100644 index 00000000000..be379e6a3be --- /dev/null +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/analysisApiUtils/getAllClassOrObjectSymbols.kt @@ -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 { + return getFileScope().getClassifierSymbols() + .filterIsInstance() + .flatMap { classSymbol -> listOf(classSymbol) + classSymbol.getAllClassOrObjectSymbols() } + .toList() +} + +context(KtAnalysisSession) +private fun KtClassOrObjectSymbol.getAllClassOrObjectSymbols(): Sequence { + return sequence { + val nestedClasses = getMemberScope().getClassifierSymbols().filterIsInstance() + yieldAll(nestedClasses) + + nestedClasses.forEach { nestedClass -> + yieldAll(nestedClass.getAllClassOrObjectSymbols()) + } + } +} \ No newline at end of file diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/analysisApiUtils/getDeclaredSuperInterfaceSymbols.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/analysisApiUtils/getDeclaredSuperInterfaceSymbols.kt new file mode 100644 index 00000000000..6cdc207cccb --- /dev/null +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/analysisApiUtils/getDeclaredSuperInterfaceSymbols.kt @@ -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 { + 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() +} \ No newline at end of file diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/analysisApiUtils/isCloneable.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/analysisApiUtils/isCloneable.kt new file mode 100644 index 00000000000..a3182bd3311 --- /dev/null +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/analysisApiUtils/isCloneable.kt @@ -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() + } \ No newline at end of file diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateSuperInterfaces.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateSuperInterfaces.kt new file mode 100644 index 00000000000..0dcd3c930a2 --- /dev/null +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateSuperInterfaces.kt @@ -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 { + return getDeclaredSuperInterfaceSymbols().mapNotNull { superInterfaceSymbol -> + superInterfaceSymbol.translateToObjCProtocol() + } +} \ No newline at end of file diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCClass.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCClass.kt index 84b6c94239f..232e042d3f0 100644 --- a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCClass.kt +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCClass.kt @@ -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.backend.konan.objcexport.* 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.getSuperClassSymbolNotAny import org.jetbrains.kotlin.objcexport.analysisApiUtils.isVisibleInObjC @@ -18,7 +17,7 @@ fun KtClassOrObjectSymbol.translateToObjCClass(): ObjCClass? { val superClass = getSuperClassSymbolNotAny() 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 final = if (this is KtSymbolWithModality) this.modality == Modality.FINAL else false val attributes = if (enumKind || final) listOf(OBJC_SUBCLASSING_RESTRICTED) else emptyList() @@ -28,9 +27,10 @@ fun KtClassOrObjectSymbol.translateToObjCClass(): ObjCClass? { val origin: ObjCExportStubOrigin = getObjCExportStubOrigin() val superProtocols: List = superProtocols() - val members: List = getAllMembers() - .sortedWith(StableSymbolOrder) + val members: List = getMemberScope().getCallableSymbols().plus(getMemberScope().getConstructors()) + .sortedWith(StableCallableOrder) .flatMap { it.translateToObjCExportStubs() } + .toList() val categoryName: String? = null val generics: List = emptyList() @@ -57,6 +57,13 @@ private fun abbreviate(name: String): String { val uppers = normalizedName.filterIndexed { index, character -> index == 0 || character.isUpperCase() } if (uppers.length >= 3) return uppers - 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") +} \ No newline at end of file diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCExportStubs.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCExportStubs.kt new file mode 100644 index 00000000000..d881f577b95 --- /dev/null +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCExportStubs.kt @@ -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 { + return when (this) { + is KtConstructorSymbol -> translateToObjCConstructors() + is KtPropertySymbol -> listOfNotNull(translateToObjCProperty()) + is KtFunctionSymbol -> listOfNotNull(translateToObjCMethod()) + else -> emptyList() + } +} \ No newline at end of file diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCHeader.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCHeader.kt index d3fb320e616..af32e45084d 100644 --- a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCHeader.kt +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCHeader.kt @@ -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. */ package org.jetbrains.kotlin.objcexport 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.objcexport.analysisApiUtils.errorForwardClass -import org.jetbrains.kotlin.objcexport.analysisApiUtils.errorInterface -import org.jetbrains.kotlin.objcexport.analysisApiUtils.hasErrorTypes +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.objcexport.analysisApiUtils.* import org.jetbrains.kotlin.psi.KtFile - +import org.jetbrains.kotlin.utils.addIfNotNull context(KtAnalysisSession, KtObjCExportSession) fun translateToObjCHeader(files: List): ObjCHeader { - val declarations = files - .sortedWith(StableFileOrder) - .flatMap { file -> file.getFileSymbol().translateToObjCExportStubs() } - .toMutableList() + val stubs = mutableListOf() + val protocolForwardDeclarations = mutableSetOf() + val classForwardDeclarations = mutableSetOf() - val classForwardDeclarations = getClassForwardDeclarations(declarations).toMutableSet() - val protocolForwardDeclarations = getProtocolForwardDeclarations(declarations) + val symbolTranslationQueue = mutableListOf() + val translatedClassifiers = mutableMapOf() - if (declarations.hasErrorTypes()) { - declarations.add(errorInterface) + fun process( + symbol: KtSymbol, + forwardProtocolsAndClasses: Boolean = false, + ): List { + val result = mutableListOf() + 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): List { + val result = mutableListOf() + symbolTranslationQueue.addAll(symbols) + while (true) { + val next = symbolTranslationQueue.removeFirstOrNull() ?: break + result.addAll(process(next)) + } + return result + } + + fun processDependencySymbols(stubs: List): List { + 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) } + protocolForwardDeclarations += stubs + .filterIsInstance() + .flatMap { it.superProtocols } + return ObjCHeader( - stubs = declarations, + stubs = stubs, classForwardDeclarations = classForwardDeclarations, 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): Set { - return declarations - .filterIsInstance() - .filter { clazz -> - clazz.members - .filterIsInstance() - .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) = declarations - .filterIsInstance() - .flatMap { it.superProtocols } - .toSet() - - -context(KtAnalysisSession, KtObjCExportSession) -fun KtFileSymbol.translateToObjCExportStubs(): List { - return listOfNotNull(translateToObjCTopLevelInterfaceFileFacade()) + getFileScope().getClassifierSymbols() - .sortedWith(StableClassifierOrder) - .flatMap { classifierSymbol -> classifierSymbol.translateToObjCExportStubs() } -} - - -context(KtAnalysisSession, KtObjCExportSession) -internal fun KtSymbol.translateToObjCExportStubs(): List { - 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() - } } \ No newline at end of file diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCObject.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCObject.kt index 72beaf28e02..da773b00599 100644 --- a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCObject.kt +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCObject.kt @@ -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.backend.konan.objcexport.* 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.getSuperClassSymbolNotAny import org.jetbrains.kotlin.objcexport.analysisApiUtils.isVisibleInObjC @@ -32,9 +31,10 @@ fun KtClassOrObjectSymbol.translateToObjCObject(): ObjCClass? { val superClassGenerics: List = emptyList() val objectMembers = getDefaultMembers() - getAllMembers().flatMap { it.translateToObjCExportStubs() }.forEach { - objectMembers.add(it) - } + getMemberScope().getCallableSymbols() + .sortedWith(StableCallableOrder) + .flatMap { it.translateToObjCExportStubs() } + .forEach { objectMembers.add(it) } return ObjCInterfaceImpl( name.objCName, @@ -97,7 +97,8 @@ private fun KtClassOrObjectSymbol.getDefaultMembers(): MutableList { - return superTypes - .asSequence() - .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 } + return getDeclaredSuperInterfaceSymbols() + .filter { superInterface -> !superInterface.isCloneable } .map { superInterface -> superInterface.getObjCClassOrProtocolName().objCName } .toList() } \ No newline at end of file diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCType.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCType.kt index b596b50bf80..ddc8a32793b 100644 --- a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCType.kt +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCType.kt @@ -4,6 +4,8 @@ import org.jetbrains.kotlin.analysis.api.KtAnalysisSession import org.jetbrains.kotlin.analysis.api.KtStarTypeProjection import org.jetbrains.kotlin.analysis.api.KtTypeArgumentWithVariance 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.types.KtErrorType 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 */ + val classSymbol: KtClassOrObjectSymbol? = if (classId != null) getClassOrObjectSymbolByClassId(classId) else null run check@{ if (classId == null) return@check - val classSymbol = getClassOrObjectSymbolByClassId(classId) ?: return@check if (classSymbol !is KtNamedClassOrObjectSymbol) return@check if (classSymbol.isInline) return ObjCIdType } @@ -112,15 +114,30 @@ private fun KtType.mapToReferenceTypeIgnoringNullability(): ObjCNonNullReference } if (fullyExpandedType is KtNonErrorClassType) { - val typeName = typesMap[classId] - ?: fullyExpandedType.classId.shortClassName.asString().getObjCKotlinStdlibClassOrProtocolName().objCName + typesMap[classId]?.let { typeName -> + 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() - 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.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 } /* diff --git a/native/objcexport-header-generator/impl/analysis-api/test/org/jetbrains/kotlin/objcexport/tests/ForwardedClassesAndProtocolsDependenciesTest.kt b/native/objcexport-header-generator/impl/analysis-api/test/org/jetbrains/kotlin/objcexport/tests/ForwardedClassesAndProtocolsDependenciesTest.kt new file mode 100644 index 00000000000..941cae767e2 --- /dev/null +++ b/native/objcexport-header-generator/impl/analysis-api/test/org/jetbrains/kotlin/objcexport/tests/ForwardedClassesAndProtocolsDependenciesTest.kt @@ -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 + """, + protocols = setOf("Iterator"), + classes = emptySet() + ) + } + + @Test + fun `test - array`() { + doTest( + code = """ + val i: Array + """, + 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, + classes: Set, + ) { + 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)) + } + } + } +} \ No newline at end of file diff --git a/native/objcexport-header-generator/impl/analysis-api/test/org/jetbrains/kotlin/objcexport/tests/GetAllClassOrObjectSymbolsTest.kt b/native/objcexport-header-generator/impl/analysis-api/test/org/jetbrains/kotlin/objcexport/tests/GetAllClassOrObjectSymbolsTest.kt new file mode 100644 index 00000000000..1ab86cf616b --- /dev/null +++ b/native/objcexport-header-generator/impl/analysis-api/test/org/jetbrains/kotlin/objcexport/tests/GetAllClassOrObjectSymbolsTest.kt @@ -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() + ) + } + } +} \ No newline at end of file diff --git a/native/objcexport-header-generator/impl/analysis-api/test/org/jetbrains/kotlin/objcexport/tests/GetSuperInterfacesTest.kt b/native/objcexport-header-generator/impl/analysis-api/test/org/jetbrains/kotlin/objcexport/tests/GetSuperInterfacesTest.kt new file mode 100644 index 00000000000..2aebee6934b --- /dev/null +++ b/native/objcexport-header-generator/impl/analysis-api/test/org/jetbrains/kotlin/objcexport/tests/GetSuperInterfacesTest.kt @@ -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() + ) + } + } +} \ No newline at end of file diff --git a/native/objcexport-header-generator/impl/analysis-api/test/org/jetbrains/kotlin/objcexport/tests/IsCloneableTest.kt b/native/objcexport-header-generator/impl/analysis-api/test/org/jetbrains/kotlin/objcexport/tests/IsCloneableTest.kt new file mode 100644 index 00000000000..3fb5c5de9b6 --- /dev/null +++ b/native/objcexport-header-generator/impl/analysis-api/test/org/jetbrains/kotlin/objcexport/tests/IsCloneableTest.kt @@ -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!! + } \ No newline at end of file diff --git a/native/objcexport-header-generator/impl/k1/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportHeaderGenerator.kt b/native/objcexport-header-generator/impl/k1/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportHeaderGenerator.kt index 3a97bff1aa2..2ce139f2708 100644 --- a/native/objcexport-header-generator/impl/k1/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportHeaderGenerator.kt +++ b/native/objcexport-header-generator/impl/k1/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportHeaderGenerator.kt @@ -197,7 +197,8 @@ abstract class ObjCExportHeaderGenerator @InternalKotlinNativeApi constructor( private fun generateInterface(descriptor: ClassDescriptor) { if (!generatedClasses.add(descriptor)) return - stubs.add(translator.translateInterface(descriptor)) + val stub = translator.translateInterface(descriptor) + stubs.add(stub) } internal fun requireClassOrInterface(descriptor: ClassDescriptor) { diff --git a/native/objcexport-header-generator/src/org/jetbrains/kotlin/backend/konan/objcexport/objcTypes.kt b/native/objcexport-header-generator/src/org/jetbrains/kotlin/backend/konan/objcexport/objcTypes.kt index e4f9e3b0e7b..49574216cd6 100644 --- a/native/objcexport-header-generator/src/org/jetbrains/kotlin/backend/konan/objcexport/objcTypes.kt +++ b/native/objcexport-header-generator/src/org/jetbrains/kotlin/backend/konan/objcexport/objcTypes.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.backend.konan.objcexport import org.jetbrains.kotlin.backend.konan.InternalKotlinNativeApi +import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.types.Variance sealed class ObjCType { @@ -25,7 +26,10 @@ data class ObjCRawType( 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() @@ -33,6 +37,9 @@ data class ObjCNullableReferenceType( val nonNullType: ObjCNonNullReferenceType, val isNullableResult: Boolean = false, ) : ObjCReferenceType() { + + override val classId: ClassId? get() = nonNullType.classId + override fun render(attrsAndName: String): String { val attribute = if (isNullableResult) objcNullableResultAttribute else objcNullableAttribute return nonNullType.render(" $attribute".withAttrsAndName(attrsAndName)) @@ -42,8 +49,10 @@ data class ObjCNullableReferenceType( data class ObjCClassType( val className: String, val typeArguments: List = emptyList(), + override val classId: ClassId? = null, ) : ObjCNonNullReferenceType() { + override fun render(attrsAndName: String) = buildString { append(className) if (typeArguments.isNotEmpty()) { @@ -66,11 +75,12 @@ sealed class ObjCGenericTypeUsage : ObjCNonNullReferenceType() { data class ObjCGenericTypeRawUsage(override val typeName: String) : ObjCGenericTypeUsage() data class ObjCGenericTypeParameterUsage( - override val typeName: String + override val typeName: String, ) : ObjCGenericTypeUsage() data class ObjCProtocolType( val protocolName: String, + override val classId: ClassId? = null, ) : ObjCNonNullReferenceType() { override fun render(attrsAndName: String) = "id<$protocolName>".withAttrsAndName(attrsAndName) } @@ -87,7 +97,6 @@ data class ObjCBlockPointerType( val returnType: ObjCType, val parameterTypes: List, ) : ObjCNonNullReferenceType() { - override fun render(attrsAndName: String) = returnType.render(buildString { append("(^") append(attrsAndName) @@ -199,7 +208,7 @@ data class ObjCGenericTypeRawDeclaration( data class ObjCGenericTypeParameterDeclaration( override val typeName: String, - override val variance: ObjCVariance + override val variance: ObjCVariance, ) : ObjCGenericTypeDeclaration() @InternalKotlinNativeApi diff --git a/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/testUtils/testDataDir.kt b/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/testUtils/testDataDir.kt index 5bed39f7169..65b3e39a3d0 100644 --- a/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/testUtils/testDataDir.kt +++ b/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/testUtils/testDataDir.kt @@ -10,4 +10,5 @@ import java.io.File val testDataDir = File("native/objcexport-header-generator/testData") val headersTestDataDir = testDataDir.resolve("headers") val baseDeclarationsDir = testDataDir.resolve("baseDeclarations") -val forwardDeclarationsDir = testDataDir.resolve("forwardDeclarations") \ No newline at end of file +val forwardDeclarationsDir = testDataDir.resolve("forwardDeclarations") +val dependenciesDir = testDataDir.resolve("dependencies") \ No newline at end of file diff --git a/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/tests/ObjCDependenciesTypesTest.kt b/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/tests/ObjCDependenciesTypesTest.kt new file mode 100644 index 00000000000..bcc12530b55 --- /dev/null +++ b/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/tests/ObjCDependenciesTypesTest.kt @@ -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) + } +} \ No newline at end of file diff --git a/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/tests/ObjCExportForwardDeclarationsTest.kt b/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/tests/ObjCExportForwardDeclarationsTest.kt index 680528fb396..e8d4943d677 100644 --- a/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/tests/ObjCExportForwardDeclarationsTest.kt +++ b/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/tests/ObjCExportForwardDeclarationsTest.kt @@ -6,7 +6,6 @@ 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.forwardDeclarationsDir import org.jetbrains.kotlin.test.KotlinTestUtils import org.junit.jupiter.api.Test @@ -18,25 +17,21 @@ class ObjCExportForwardDeclarationsTest( ) { @Test - @TodoAnalysisApi fun `test - function returning interface`() { doTest(forwardDeclarationsDir.resolve("functionReturningInterface")) } @Test - @TodoAnalysisApi fun `test - function returning class`() { doTest(forwardDeclarationsDir.resolve("functionReturningClass")) } @Test - @TodoAnalysisApi fun `test - property returning interface`() { doTest(forwardDeclarationsDir.resolve("propertyReturningInterface")) } @Test - @TodoAnalysisApi fun `test - property returning class`() { doTest(forwardDeclarationsDir.resolve("propertyReturningClass")) } diff --git a/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/tests/ObjCExportHeaderGeneratorTest.kt b/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/tests/ObjCExportHeaderGeneratorTest.kt index 403722dfc8d..b0550065fb4 100644 --- a/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/tests/ObjCExportHeaderGeneratorTest.kt +++ b/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/tests/ObjCExportHeaderGeneratorTest.kt @@ -70,6 +70,9 @@ class ObjCExportHeaderGeneratorTest(private val generator: HeaderGenerator) { doTest(headersTestDataDir.resolve("sameClassNameInDifferentPackage")) } + /** + * Naming of nested classes: KT-65633 + */ @Test @TodoAnalysisApi fun `test - nestedClass`() { @@ -83,7 +86,6 @@ class ObjCExportHeaderGeneratorTest(private val generator: HeaderGenerator) { } @Test - @TodoAnalysisApi // Early translate of forward declarations fun `test - classImplementingInterface`() { doTest(headersTestDataDir.resolve("classImplementingInterface")) } @@ -178,7 +180,6 @@ class ObjCExportHeaderGeneratorTest(private val generator: HeaderGenerator) { } @Test - @TodoAnalysisApi // Early translate of forward declarations fun `test - classWithUnresolvedSuperTypeGenerics`() { doTest(headersTestDataDir.resolve("classWithUnresolvedSuperTypeGenerics")) } @@ -198,6 +199,40 @@ class ObjCExportHeaderGeneratorTest(private val generator: HeaderGenerator) { 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()) { if (!root.isDirectory) fail("Expected ${root.absolutePath} to be directory") val generatedHeaders = generator.generateHeaders(root, configuration).toString() diff --git a/native/objcexport-header-generator/testData/dependencies/array/!array.h b/native/objcexport-header-generator/testData/dependencies/array/!array.h new file mode 100644 index 00000000000..66f6ab68d21 --- /dev/null +++ b/native/objcexport-header-generator/testData/dependencies/array/!array.h @@ -0,0 +1,49 @@ +#import +#import +#import +#import +#import +#import +#import + +@class Array; + +@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 *a __attribute__((swift_name("a"))); +@end + +__attribute__((objc_subclassing_restricted)) +@interface Array : 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 __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 diff --git a/native/objcexport-header-generator/testData/dependencies/array/Foo.kt b/native/objcexport-header-generator/testData/dependencies/array/Foo.kt new file mode 100644 index 00000000000..453f3a6a0de --- /dev/null +++ b/native/objcexport-header-generator/testData/dependencies/array/Foo.kt @@ -0,0 +1 @@ +val a: Array \ No newline at end of file diff --git a/native/objcexport-header-generator/testData/dependencies/iterator/!iterator.h b/native/objcexport-header-generator/testData/dependencies/iterator/!iterator.h new file mode 100644 index 00000000000..302893b6b45 --- /dev/null +++ b/native/objcexport-header-generator/testData/dependencies/iterator/!iterator.h @@ -0,0 +1,36 @@ +#import +#import +#import +#import +#import +#import +#import + +@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 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 diff --git a/native/objcexport-header-generator/testData/dependencies/iterator/Foo.kt b/native/objcexport-header-generator/testData/dependencies/iterator/Foo.kt new file mode 100644 index 00000000000..625904aab88 --- /dev/null +++ b/native/objcexport-header-generator/testData/dependencies/iterator/Foo.kt @@ -0,0 +1 @@ +val a: Iterator \ No newline at end of file diff --git a/native/objcexport-header-generator/testData/dependencies/stringBuilder/!stringBuilder.h b/native/objcexport-header-generator/testData/dependencies/stringBuilder/!stringBuilder.h new file mode 100644 index 00000000000..596d918a3c3 --- /dev/null +++ b/native/objcexport-header-generator/testData/dependencies/stringBuilder/!stringBuilder.h @@ -0,0 +1,194 @@ +#import +#import +#import +#import +#import +#import +#import + +@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)appendValue:(unichar)value __attribute__((swift_name("append(value:)"))); +- (id)appendValue_:(id _Nullable)value __attribute__((swift_name("append(value_:)"))); +- (id)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 +- (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 +- (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 diff --git a/native/objcexport-header-generator/testData/dependencies/stringBuilder/Foo.kt b/native/objcexport-header-generator/testData/dependencies/stringBuilder/Foo.kt new file mode 100644 index 00000000000..81908f43121 --- /dev/null +++ b/native/objcexport-header-generator/testData/dependencies/stringBuilder/Foo.kt @@ -0,0 +1 @@ +val a: StringBuilder \ No newline at end of file diff --git a/native/objcexport-header-generator/testData/headers/classReferencingDependencyClassAsReturnType/!classReferencingDependencyClassAsReturnType.h b/native/objcexport-header-generator/testData/headers/classReferencingDependencyClassAsReturnType/!classReferencingDependencyClassAsReturnType.h new file mode 100644 index 00000000000..bb82bec8de6 --- /dev/null +++ b/native/objcexport-header-generator/testData/headers/classReferencingDependencyClassAsReturnType/!classReferencingDependencyClassAsReturnType.h @@ -0,0 +1,47 @@ +#import +#import +#import +#import +#import +#import +#import + +@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 +- (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 diff --git a/native/objcexport-header-generator/testData/headers/classReferencingDependencyClassAsReturnType/Foo.kt b/native/objcexport-header-generator/testData/headers/classReferencingDependencyClassAsReturnType/Foo.kt new file mode 100644 index 00000000000..dff95a3055c --- /dev/null +++ b/native/objcexport-header-generator/testData/headers/classReferencingDependencyClassAsReturnType/Foo.kt @@ -0,0 +1,3 @@ +class Foo { + fun foo(): IntIterator = error("stub") +} \ No newline at end of file diff --git a/native/objcexport-header-generator/testData/headers/classReferencingOtherClassAsReturnType/!classReferencingOtherClassAsReturnType.h b/native/objcexport-header-generator/testData/headers/classReferencingOtherClassAsReturnType/!classReferencingOtherClassAsReturnType.h new file mode 100644 index 00000000000..3b1b8535250 --- /dev/null +++ b/native/objcexport-header-generator/testData/headers/classReferencingOtherClassAsReturnType/!classReferencingOtherClassAsReturnType.h @@ -0,0 +1,38 @@ +#import +#import +#import +#import +#import +#import +#import + +@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 diff --git a/native/objcexport-header-generator/testData/headers/classReferencingOtherClassAsReturnType/Foo.kt b/native/objcexport-header-generator/testData/headers/classReferencingOtherClassAsReturnType/Foo.kt new file mode 100644 index 00000000000..7e1981387b8 --- /dev/null +++ b/native/objcexport-header-generator/testData/headers/classReferencingOtherClassAsReturnType/Foo.kt @@ -0,0 +1,5 @@ +class A { + fun foo(): B = B() +} + +class B \ No newline at end of file diff --git a/native/objcexport-header-generator/testData/headers/extensionFunctions/!extensionFunctions.h b/native/objcexport-header-generator/testData/headers/extensionFunctions/!extensionFunctions.h new file mode 100644 index 00000000000..e7524ce886b --- /dev/null +++ b/native/objcexport-header-generator/testData/headers/extensionFunctions/!extensionFunctions.h @@ -0,0 +1,36 @@ +#import +#import +#import +#import +#import +#import +#import + +@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 diff --git a/native/objcexport-header-generator/testData/headers/extensionFunctions/Foo.kt b/native/objcexport-header-generator/testData/headers/extensionFunctions/Foo.kt new file mode 100644 index 00000000000..bc3042b3a6d --- /dev/null +++ b/native/objcexport-header-generator/testData/headers/extensionFunctions/Foo.kt @@ -0,0 +1,5 @@ +class Foo { + fun funA() {} +} + +fun Foo.funB() {} diff --git a/native/objcexport-header-generator/testData/headers/interfaceImplementingInterfaceOrder/!interfaceImplementingInterfaceOrder.h b/native/objcexport-header-generator/testData/headers/interfaceImplementingInterfaceOrder/!interfaceImplementingInterfaceOrder.h new file mode 100644 index 00000000000..3e24dea066e --- /dev/null +++ b/native/objcexport-header-generator/testData/headers/interfaceImplementingInterfaceOrder/!interfaceImplementingInterfaceOrder.h @@ -0,0 +1,33 @@ +#import +#import +#import +#import +#import +#import +#import + +@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 +@required +@end + +#pragma pop_macro("_Nullable_result") +#pragma clang diagnostic pop +NS_ASSUME_NONNULL_END diff --git a/native/objcexport-header-generator/testData/headers/interfaceImplementingInterfaceOrder/Foo.kt b/native/objcexport-header-generator/testData/headers/interfaceImplementingInterfaceOrder/Foo.kt new file mode 100644 index 00000000000..e1cf18b4689 --- /dev/null +++ b/native/objcexport-header-generator/testData/headers/interfaceImplementingInterfaceOrder/Foo.kt @@ -0,0 +1,3 @@ +interface A : B + +interface B \ No newline at end of file diff --git a/native/objcexport-header-generator/testData/headers/interfaceReferencingOtherInterfaceAsReturnType/!interfaceReferencingOtherInterfaceAsReturnType.h b/native/objcexport-header-generator/testData/headers/interfaceReferencingOtherInterfaceAsReturnType/!interfaceReferencingOtherInterfaceAsReturnType.h new file mode 100644 index 00000000000..b0df59f4791 --- /dev/null +++ b/native/objcexport-header-generator/testData/headers/interfaceReferencingOtherInterfaceAsReturnType/!interfaceReferencingOtherInterfaceAsReturnType.h @@ -0,0 +1,34 @@ +#import +#import +#import +#import +#import +#import +#import + +@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)foo __attribute__((swift_name("foo()"))); +@end + +@protocol B +@required +@end + +#pragma pop_macro("_Nullable_result") +#pragma clang diagnostic pop +NS_ASSUME_NONNULL_END diff --git a/native/objcexport-header-generator/testData/headers/interfaceReferencingOtherInterfaceAsReturnType/Foo.kt b/native/objcexport-header-generator/testData/headers/interfaceReferencingOtherInterfaceAsReturnType/Foo.kt new file mode 100644 index 00000000000..a0832a1bc29 --- /dev/null +++ b/native/objcexport-header-generator/testData/headers/interfaceReferencingOtherInterfaceAsReturnType/Foo.kt @@ -0,0 +1,5 @@ +interface A { + fun foo(): B +} + +interface B \ No newline at end of file diff --git a/native/objcexport-header-generator/testData/headers/nestedClass/!nestedClass.h b/native/objcexport-header-generator/testData/headers/nestedClass/!nestedClass.h index 5f14e624838..54bc5a3dbed 100644 --- a/native/objcexport-header-generator/testData/headers/nestedClass/!nestedClass.h +++ b/native/objcexport-header-generator/testData/headers/nestedClass/!nestedClass.h @@ -25,8 +25,29 @@ __attribute__((objc_subclassing_restricted)) @end __attribute__((objc_subclassing_restricted)) -__attribute__((swift_name("A.B"))) -@interface AB : Base +__attribute__((swift_name("A.A1"))) +@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)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); @end diff --git a/native/objcexport-header-generator/testData/headers/nestedClass/Foo.kt b/native/objcexport-header-generator/testData/headers/nestedClass/Foo.kt index 4e610ee6db7..032a4f2d262 100644 --- a/native/objcexport-header-generator/testData/headers/nestedClass/Foo.kt +++ b/native/objcexport-header-generator/testData/headers/nestedClass/Foo.kt @@ -1,3 +1,8 @@ class A { - class B + class A1 { + class B1 + } + class A2 { + class B2 + } } \ No newline at end of file