[ObjCExport] AA: Support additional module name prefixes
^KT-65670 Fixed
This commit is contained in:
committed by
Space Team
parent
7ee2903e15
commit
436e16efd8
@@ -15,6 +15,7 @@ dependencies {
|
||||
api(project(":compiler:psi"))
|
||||
api(project(":native:objcexport-header-generator"))
|
||||
implementation(project(":core:compiler.common.native"))
|
||||
implementation(project(":kotlin-util-klib"))
|
||||
|
||||
testImplementation(projectTests(":native:objcexport-header-generator"))
|
||||
testApi(project(":analysis:analysis-api-standalone"))
|
||||
|
||||
+15
@@ -5,6 +5,9 @@
|
||||
|
||||
package org.jetbrains.kotlin.objcexport
|
||||
|
||||
import org.jetbrains.kotlin.analysis.project.structure.KtLibraryModule
|
||||
import org.jetbrains.kotlin.analysis.project.structure.KtSourceModule
|
||||
|
||||
data class KtObjCExportConfiguration(
|
||||
/**
|
||||
* Also used as top level prefix for declarations if present
|
||||
@@ -28,4 +31,16 @@ data class KtObjCExportConfiguration(
|
||||
* (see [org.jetbrains.kotlin.objcexport.objCBaseDeclarations]).
|
||||
*/
|
||||
val generateBaseDeclarationStubs: Boolean = true,
|
||||
|
||||
/**
|
||||
* The name of modules that are to be exported in this session.
|
||||
* An exported library shall be read, and its entire API surface shall be translated and presented in the
|
||||
* objc header at the end.
|
||||
*
|
||||
* Libraries/Modules that are not listed in this [exportedModuleNames] will only export types that are used in either source code
|
||||
* or other exported libraries public surface
|
||||
* (see [KtLibraryModule.libraryName], [KtSourceModule.moduleName])
|
||||
*/
|
||||
val exportedModuleNames: Set<String> = emptySet(),
|
||||
)
|
||||
|
||||
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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.project.structure.KtLibraryModule
|
||||
import org.jetbrains.kotlin.analysis.project.structure.KtModule
|
||||
import org.jetbrains.kotlin.analysis.project.structure.KtSourceModule
|
||||
import org.jetbrains.kotlin.library.ToolingSingleFileKlibResolveStrategy
|
||||
import org.jetbrains.kotlin.library.shortName
|
||||
import org.jetbrains.kotlin.library.uniqueName
|
||||
import org.jetbrains.kotlin.util.DummyLogger
|
||||
import kotlin.io.path.extension
|
||||
import kotlin.io.path.isDirectory
|
||||
import org.jetbrains.kotlin.konan.file.File as KonanFile
|
||||
|
||||
interface KtObjCExportModuleNaming {
|
||||
context(KtAnalysisSession)
|
||||
fun getModuleName(module: KtModule): String?
|
||||
|
||||
companion object {
|
||||
val default = KtObjCExportModuleNaming(listOf(KtKlibObjCExportModuleNaming, KtSimpleObjCExportModuleNaming))
|
||||
}
|
||||
}
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
internal fun KtModule.getObjCKotlinModuleName(): String? {
|
||||
return cached(GetObjCKotlinModuleNameCacheKey(this)) {
|
||||
internal.moduleNaming.getModuleName(this)
|
||||
}
|
||||
}
|
||||
|
||||
private class GetObjCKotlinModuleNameCacheKey(private val module: KtModule) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other === this) return true
|
||||
if (other !is GetObjCKotlinModuleNameCacheKey) return false
|
||||
return this.module == other.module
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return module.hashCode()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines several [implementations] to a single [KtObjCExportModuleNaming].
|
||||
* The order of [implementations] matters: The first implementation to resopnd with a module name will win.
|
||||
*/
|
||||
fun KtObjCExportModuleNaming(implementations: List<KtObjCExportModuleNaming>): KtObjCExportModuleNaming {
|
||||
return KtCompositeObjCExportModuleNaming(implementations)
|
||||
}
|
||||
|
||||
internal object KtKlibObjCExportModuleNaming : KtObjCExportModuleNaming {
|
||||
context(KtAnalysisSession)
|
||||
override fun getModuleName(module: KtModule): String? {
|
||||
/*
|
||||
In this implementation, we're actually looking into the klib file, trying to resolve
|
||||
the contained manifest to get the 'shortName' or 'uniqueName'.
|
||||
|
||||
This information is theoretically available already (as also used by the Analysis Api), but not yet accessible.
|
||||
*/
|
||||
if (module !is KtLibraryModule) return null
|
||||
val binaryRoot = module.getBinaryRoots().singleOrNull() ?: return null
|
||||
if (!binaryRoot.isDirectory() && binaryRoot.extension != "klib") return null
|
||||
val library = runCatching { ToolingSingleFileKlibResolveStrategy.tryResolve(KonanFile(binaryRoot), DummyLogger) }
|
||||
.getOrElse { error -> error.printStackTrace(); return null } ?: return null
|
||||
return library.shortName ?: library.uniqueName
|
||||
}
|
||||
}
|
||||
|
||||
internal object KtSimpleObjCExportModuleNaming : KtObjCExportModuleNaming {
|
||||
context(KtAnalysisSession)
|
||||
override fun getModuleName(module: KtModule): String? {
|
||||
return when (module) {
|
||||
is KtSourceModule -> module.stableModuleName ?: module.moduleName
|
||||
is KtLibraryModule -> module.libraryName
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class KtCompositeObjCExportModuleNaming(private val implementations: List<KtObjCExportModuleNaming>) : KtObjCExportModuleNaming {
|
||||
context(KtAnalysisSession) override fun getModuleName(module: KtModule): String? {
|
||||
return implementations.firstNotNullOfOrNull { implementation -> implementation.getModuleName(module) }
|
||||
}
|
||||
}
|
||||
+25
-4
@@ -12,25 +12,46 @@ sealed interface KtObjCExportSession {
|
||||
val configuration: KtObjCExportConfiguration
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal representation of [KtObjCExportSession].
|
||||
* All *internal* accessible services shall be added here.
|
||||
*/
|
||||
internal sealed interface KtObjCExportSessionInternal : KtObjCExportSession {
|
||||
val moduleNaming: KtObjCExportModuleNaming
|
||||
}
|
||||
|
||||
internal val KtObjCExportSession.internal: KtObjCExportSessionInternal
|
||||
get() = when (this) {
|
||||
is KtObjCExportSessionInternal -> this
|
||||
}
|
||||
|
||||
/**
|
||||
* Private representation of [KtObjCExportSession].
|
||||
* All *private* accessible data shall only be added here and potentially
|
||||
* exposed as functions within this source file
|
||||
*/
|
||||
private interface KtObjCExportSessionPrivate : KtObjCExportSessionInternal {
|
||||
val cache: MutableMap<Any, Any?>
|
||||
}
|
||||
|
||||
private val KtObjCExportSession.private: KtObjCExportSessionPrivate
|
||||
get() = when (this) {
|
||||
is KtObjCExportSessionPrivate -> this
|
||||
}
|
||||
|
||||
private interface KtObjCExportSessionPrivate : KtObjCExportSession {
|
||||
val cache: MutableMap<Any, Any?>
|
||||
}
|
||||
|
||||
inline fun <T> KtObjCExportSession(
|
||||
configuration: KtObjCExportConfiguration,
|
||||
moduleNaming: KtObjCExportModuleNaming = KtObjCExportModuleNaming.default,
|
||||
block: KtObjCExportSession.() -> T,
|
||||
): T {
|
||||
return KtObjCExportSessionImpl(configuration, hashMapOf()).block()
|
||||
return KtObjCExportSessionImpl(configuration, moduleNaming, hashMapOf()).block()
|
||||
}
|
||||
|
||||
@PublishedApi
|
||||
internal class KtObjCExportSessionImpl(
|
||||
override val configuration: KtObjCExportConfiguration,
|
||||
override val moduleNaming: KtObjCExportModuleNaming,
|
||||
override val cache: MutableMap<Any, Any?>,
|
||||
) : KtObjCExportSessionPrivate
|
||||
|
||||
|
||||
+34
-9
@@ -6,10 +6,7 @@
|
||||
package org.jetbrains.kotlin.objcexport
|
||||
|
||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtClassKind
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtClassLikeSymbol
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.nameOrAnonymous
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.*
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportClassOrProtocolName
|
||||
import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeAsciiOnly
|
||||
|
||||
@@ -37,8 +34,11 @@ private fun KtClassLikeSymbol.getObjCName(
|
||||
return containingClass.getObjCName() + objCName.capitalizeAsciiOnly()
|
||||
}
|
||||
|
||||
// KT-65670: Append module specific prefixes?
|
||||
return configuration.frameworkName.orEmpty() + objCName
|
||||
return buildString {
|
||||
configuration.frameworkName?.let(::append)
|
||||
getObjCModuleNamePrefix()?.let(::append)
|
||||
append(objCName)
|
||||
}
|
||||
}
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
@@ -71,9 +71,10 @@ private fun KtClassLikeSymbol.getSwiftName(
|
||||
}
|
||||
}
|
||||
|
||||
// KT-65670: Append module specific prefixes?
|
||||
return swiftName
|
||||
|
||||
return buildString {
|
||||
getObjCModuleNamePrefix()?.let(::append)
|
||||
append(swiftName)
|
||||
}
|
||||
}
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
@@ -111,4 +112,28 @@ private fun KtClassLikeSymbol.canBeOuterSwift(): Boolean {
|
||||
private fun mangleSwiftNestedClassName(name: String): String = when (name) {
|
||||
"Type" -> "${name}_" // See https://github.com/JetBrains/kotlin-native/issues/3167
|
||||
else -> name
|
||||
}
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
private fun KtSymbol.getObjCModuleNamePrefix(): String? {
|
||||
val module = getContainingModule()
|
||||
val moduleName = module.getObjCKotlinModuleName() ?: return null
|
||||
if(moduleName == "stdlib" || moduleName == "kotlin-stdlib-common") return "Kotlin"
|
||||
if (moduleName in configuration.exportedModuleNames) return null
|
||||
return abbreviateModuleName(moduleName)
|
||||
}
|
||||
|
||||
/**
|
||||
* 'MyModuleName' -> 'MMN'
|
||||
* 'someLibraryFoo' -> 'SLF'
|
||||
*/
|
||||
internal fun abbreviateModuleName(name: String): String {
|
||||
val normalizedName = name
|
||||
.capitalizeAsciiOnly()
|
||||
.replace("[-.]".toRegex(), "_")
|
||||
|
||||
val uppers = normalizedName.filter { character -> character.isUpperCase() }
|
||||
if (uppers.length >= 3) return uppers
|
||||
|
||||
return normalizedName
|
||||
}
|
||||
+2
-1
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtFileSymbol
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCInterface
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCInterfaceImpl
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.toNameAttributes
|
||||
import org.jetbrains.kotlin.objcexport.analysisApiUtils.getDefaultSuperClassOrProtocolName
|
||||
|
||||
|
||||
@@ -59,7 +60,7 @@ fun KtFileSymbol.getTopLevelFacade(): ObjCInterface? {
|
||||
name = fileName.objCName,
|
||||
comment = null,
|
||||
origin = null,
|
||||
attributes = listOf(OBJC_SUBCLASSING_RESTRICTED),
|
||||
attributes = listOf(OBJC_SUBCLASSING_RESTRICTED) + fileName.toNameAttributes(),
|
||||
superProtocols = emptyList(),
|
||||
members = extensions.mapNotNull { it.translateToObjCExportStub() },
|
||||
categoryName = null,
|
||||
|
||||
+3
-1
@@ -30,6 +30,7 @@ class AnalysisApiHeaderGeneratorExtension : ParameterResolver {
|
||||
object AnalysisApiHeaderGenerator : HeaderGenerator {
|
||||
override fun generateHeaders(root: File, configuration: HeaderGenerator.Configuration): ObjCHeader {
|
||||
val session = createStandaloneAnalysisApiSession(
|
||||
kotlinSourceModuleName = defaultKotlinSourceModuleName,
|
||||
kotlinFiles = root.listFiles().orEmpty().filter { it.extension == "kt" },
|
||||
dependencyKlibs = configuration.dependencies
|
||||
)
|
||||
@@ -39,7 +40,8 @@ object AnalysisApiHeaderGenerator : HeaderGenerator {
|
||||
KtObjCExportSession(
|
||||
KtObjCExportConfiguration(
|
||||
frameworkName = configuration.frameworkName,
|
||||
generateBaseDeclarationStubs = configuration.generateBaseDeclarationStubs
|
||||
generateBaseDeclarationStubs = configuration.generateBaseDeclarationStubs,
|
||||
exportedModuleNames = setOf(defaultKotlinSourceModuleName)
|
||||
)
|
||||
) {
|
||||
translateToObjCHeader(files.map { it as KtFile })
|
||||
|
||||
+2
-2
@@ -77,14 +77,14 @@ class InlineSourceCodeAnalysisExtension : ParameterResolver, AfterEachCallback {
|
||||
*/
|
||||
private class InlineSourceCodeAnalysisImpl(private val tempDir: File) : InlineSourceCodeAnalysis {
|
||||
override fun createKtFile(@Language("kotlin") sourceCode: String): KtFile {
|
||||
return createStandaloneAnalysisApiSession(tempDir, mapOf("TestSources.kt" to sourceCode))
|
||||
return createStandaloneAnalysisApiSession(tempDir = tempDir, kotlinSources = mapOf("TestSources.kt" to sourceCode))
|
||||
.modulesWithFiles.entries.single()
|
||||
.value.single() as KtFile
|
||||
}
|
||||
|
||||
override fun createKtFiles(builder: InlineSourceCodeAnalysis.KtModuleBuilder.() -> Unit): Map<String, KtFile> {
|
||||
val sources = KtModuleBuilderImpl().also(builder).sources.toMap()
|
||||
return createStandaloneAnalysisApiSession(tempDir, sources)
|
||||
return createStandaloneAnalysisApiSession(tempDir = tempDir, kotlinSources = sources)
|
||||
.modulesWithFiles.entries.single()
|
||||
.value.map { it as KtFile }
|
||||
.associateBy { it.name }
|
||||
|
||||
+3
-1
@@ -13,7 +13,9 @@ import org.jetbrains.kotlin.psi.KtElement
|
||||
|
||||
inline fun <T> analyzeWithObjCExport(
|
||||
useSiteKtElement: KtElement,
|
||||
configuration: KtObjCExportConfiguration = KtObjCExportConfiguration(),
|
||||
configuration: KtObjCExportConfiguration = KtObjCExportConfiguration(
|
||||
exportedModuleNames = setOf(defaultKotlinSourceModuleName)
|
||||
),
|
||||
action: context(KtAnalysisSession, KtObjCExportSession) () -> T,
|
||||
): T = analyze(useSiteKtElement) {
|
||||
KtObjCExportSession(configuration) {
|
||||
|
||||
+10
-3
@@ -20,11 +20,14 @@ import java.nio.file.Path
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.nameWithoutExtension
|
||||
|
||||
const val defaultKotlinSourceModuleName = "testModule"
|
||||
|
||||
/**
|
||||
* Creates a standalone analysis session from Kotlin source code passed as [kotlinSources]
|
||||
*/
|
||||
fun createStandaloneAnalysisApiSession(
|
||||
tempDir: File,
|
||||
kotlinSourceModuleName: String = defaultKotlinSourceModuleName,
|
||||
kotlinSources: Map</* File Name */ String, /* Source Code */ String>,
|
||||
dependencyKlibs: List<Path> = emptyList(),
|
||||
): StandaloneAnalysisAPISession {
|
||||
@@ -36,14 +39,18 @@ fun createStandaloneAnalysisApiSession(
|
||||
writeText(sourceCode)
|
||||
}
|
||||
}
|
||||
return createStandaloneAnalysisApiSession(listOf(testModuleRoot), dependencyKlibs)
|
||||
return createStandaloneAnalysisApiSession(kotlinSourceModuleName, listOf(testModuleRoot), dependencyKlibs)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a standalone analysis session from [kotlinFiles] on disk.
|
||||
* The Kotlin/Native stdlib will be provided as dependency
|
||||
*/
|
||||
fun createStandaloneAnalysisApiSession(kotlinFiles: List<File>, dependencyKlibs: List<Path> = emptyList()): StandaloneAnalysisAPISession {
|
||||
fun createStandaloneAnalysisApiSession(
|
||||
kotlinSourceModuleName: String = defaultKotlinSourceModuleName,
|
||||
kotlinFiles: List<File>,
|
||||
dependencyKlibs: List<Path> = emptyList(),
|
||||
): StandaloneAnalysisAPISession {
|
||||
val currentArchitectureTarget = HostManager.host
|
||||
val nativePlatform = NativePlatforms.nativePlatformByTargets(listOf(currentArchitectureTarget))
|
||||
return buildStandaloneAnalysisAPISession {
|
||||
@@ -77,7 +84,7 @@ fun createStandaloneAnalysisApiSession(kotlinFiles: List<File>, dependencyKlibs:
|
||||
addRegularDependency(dependencyKlibModule)
|
||||
}
|
||||
platform = nativePlatform
|
||||
moduleName = "source"
|
||||
moduleName = kotlinSourceModuleName
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.objcexport.abbreviateModuleName
|
||||
import org.junit.jupiter.api.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class AbbreviateModuleNameTest {
|
||||
@Test
|
||||
fun `test - empty string`() {
|
||||
assertEquals("", abbreviateModuleName(""))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - simple name`() {
|
||||
assertEquals("Foo", abbreviateModuleName("Foo"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - simple lowercase name`() {
|
||||
assertEquals("Foo", abbreviateModuleName("foo"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - longer module name`() {
|
||||
assertEquals("LMN", abbreviateModuleName("LongModuleName"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - longer module name - starting lowercase`() {
|
||||
assertEquals("LMN", abbreviateModuleName("longModuleName"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - very long module name`() {
|
||||
assertEquals("TIAVLMN", abbreviateModuleName("thisIsAVeryLongModuleName"))
|
||||
}
|
||||
}
|
||||
+7
-6
@@ -6,6 +6,7 @@ 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.testUtils.defaultKotlinSourceModuleName
|
||||
import org.jetbrains.kotlin.objcexport.translateToObjCHeader
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.junit.jupiter.api.Test
|
||||
@@ -24,7 +25,7 @@ class ForwardedClassesAndProtocolsDependenciesTest(
|
||||
code = """
|
||||
val i: Iterator<Int>
|
||||
""",
|
||||
protocols = setOf("Iterator"),
|
||||
protocols = setOf("KotlinIterator"),
|
||||
classes = emptySet()
|
||||
)
|
||||
}
|
||||
@@ -35,8 +36,8 @@ class ForwardedClassesAndProtocolsDependenciesTest(
|
||||
code = """
|
||||
val i: Array<Int>
|
||||
""",
|
||||
protocols = setOf("Iterator"),
|
||||
classes = setOf("Array")
|
||||
protocols = setOf("KotlinIterator"),
|
||||
classes = setOf("KotlinArray")
|
||||
)
|
||||
}
|
||||
|
||||
@@ -46,8 +47,8 @@ class ForwardedClassesAndProtocolsDependenciesTest(
|
||||
code = """
|
||||
val i: StringBuilder
|
||||
""",
|
||||
protocols = setOf("CharSequence", "Appendable", "Iterator"),
|
||||
classes = setOf("StringBuilder", "CharArray", "CharIterator")
|
||||
protocols = setOf("KotlinCharSequence", "KotlinAppendable", "KotlinIterator"),
|
||||
classes = setOf("KotlinStringBuilder", "KotlinCharArray", "KotlinCharIterator")
|
||||
)
|
||||
}
|
||||
|
||||
@@ -81,7 +82,7 @@ class ForwardedClassesAndProtocolsDependenciesTest(
|
||||
|
||||
private fun translateClassesAndProtocols(file: KtFile): ObjCHeader {
|
||||
return analyze(file) {
|
||||
KtObjCExportSession(KtObjCExportConfiguration()) {
|
||||
KtObjCExportSession(KtObjCExportConfiguration(exportedModuleNames = setOf(defaultKotlinSourceModuleName))) {
|
||||
translateToObjCHeader(listOf(file))
|
||||
}
|
||||
}
|
||||
|
||||
+4
@@ -83,6 +83,10 @@ fun ObjCExportClassOrProtocolName.toNameAttributes(): List<String> = listOfNotNu
|
||||
swiftName.takeIf { it != objCName }?.let { swiftNameAttribute(it) }
|
||||
)
|
||||
|
||||
fun ObjCExportFileName.toNameAttributes(): List<String> = listOfNotNull(
|
||||
swiftName.takeIf { it != objCName }?.let { swiftNameAttribute(it) }
|
||||
)
|
||||
|
||||
@InternalKotlinNativeApi
|
||||
fun swiftNameAttribute(swiftName: String) = "swift_name(\"$swiftName\")"
|
||||
|
||||
|
||||
-1
@@ -148,7 +148,6 @@ class ObjCExportHeaderGeneratorTest(private val generator: HeaderGenerator) {
|
||||
}
|
||||
|
||||
@Test
|
||||
@TodoAnalysisApi
|
||||
fun `test - functionWithErrorTypeAndFrameworkName`() {
|
||||
doTest(headersTestDataDir.resolve("functionWithErrorTypeAndFrameworkName"), Configuration(frameworkName = "shared"))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user