diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BinaryOptions.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BinaryOptions.kt index fd20843a80f..b7c51b46932 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BinaryOptions.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BinaryOptions.kt @@ -37,6 +37,8 @@ object BinaryOptions : BinaryOptionRegistry() { val objcExportReportNameCollisions by booleanOption() + val objcExportErrorOnNameCollisions by booleanOption() + val gc by option(shortcut = { it.shortcut }) val gcSchedulerType by option(hideValue = { it.deprecatedWithReplacement != null }) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanCompilerFrontendServices.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanCompilerFrontendServices.kt index 93081b79de4..5e995ac34e3 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanCompilerFrontendServices.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanCompilerFrontendServices.kt @@ -24,6 +24,8 @@ internal fun StorageComponentContainer.initContainer(config: KonanConfig) { useInstance(object : ObjCExportProblemCollector { override fun reportWarning(text: String) {} override fun reportWarning(declaration: DeclarationDescriptor, text: String) {} + override fun reportError(text: String) {} + override fun reportError(declaration: DeclarationDescriptor, text: String) {} override fun reportException(throwable: Throwable) = throw throwable }) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExport.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExport.kt index 3721857af66..94339641408 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExport.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExport.kt @@ -52,6 +52,7 @@ internal fun produceObjCExportInterface( val disableSwiftMemberNameMangling = config.configuration.getBoolean(BinaryOptions.objcExportDisableSwiftMemberNameMangling) val ignoreInterfaceMethodCollisions = config.configuration.getBoolean(BinaryOptions.objcExportIgnoreInterfaceMethodCollisions) val reportNameCollisions = config.configuration.getBoolean(BinaryOptions.objcExportReportNameCollisions) + val errorOnNameCollisions = config.configuration.getBoolean(BinaryOptions.objcExportErrorOnNameCollisions) val problemCollector = ObjCExportCompilerProblemCollector(context) @@ -65,7 +66,11 @@ internal fun produceObjCExportInterface( objcGenerics = objcGenerics, disableSwiftMemberNameMangling = disableSwiftMemberNameMangling, ignoreInterfaceMethodCollisions = ignoreInterfaceMethodCollisions, - reportNameCollisions = reportNameCollisions, + nameCollisionMode = when { + errorOnNameCollisions -> ObjCExportNameCollisionMode.ERROR + reportNameCollisions -> ObjCExportNameCollisionMode.WARNING + else -> ObjCExportNameCollisionMode.NONE + } ) val shouldExportKDoc = context.shouldExportKDoc() val additionalImports = context.config.configuration.getNotNull(KonanConfigKeys.FRAMEWORK_IMPORT_HEADERS) @@ -77,21 +82,33 @@ internal fun produceObjCExportInterface( } private class ObjCExportCompilerProblemCollector(val context: PhaseContext) : ObjCExportProblemCollector { + private val DeclarationDescriptor.psiLocation + get() = (this@psiLocation as? DeclarationDescriptorWithSource)?.source?.getPsi()?.let { MessageUtil.psiElementToMessageLocation(it) } + override fun reportWarning(text: String) { context.reportCompilationWarning(text) } override fun reportWarning(declaration: DeclarationDescriptor, text: String) { - val psi = (declaration as? DeclarationDescriptorWithSource)?.source?.getPsi() - ?: return reportWarning( - "$text\n (at ${DescriptorRenderer.COMPACT_WITH_SHORT_TYPES.render(declaration)})" - ) - - val location = MessageUtil.psiElementToMessageLocation(psi) + val location = declaration.psiLocation ?: return reportWarning( + "$text\n (at ${DescriptorRenderer.COMPACT_WITH_SHORT_TYPES.render(declaration)})" + ) context.messageCollector.report(CompilerMessageSeverity.WARNING, text, location) } + override fun reportError(text: String) { + context.messageCollector.report(CompilerMessageSeverity.ERROR, text, null) + } + + override fun reportError(declaration: DeclarationDescriptor, text: String) { + val location = declaration.psiLocation ?: return reportError( + "$text\n (at ${DescriptorRenderer.COMPACT_WITH_SHORT_TYPES.render(declaration)})" + ) + + context.messageCollector.report(CompilerMessageSeverity.ERROR, text, location) + } + override fun reportException(throwable: Throwable) { throw throwable } diff --git a/native/base/src/main/kotlin/org/jetbrains/kotlin/backend/konan/ObjCExportNameCollisionMode.kt b/native/base/src/main/kotlin/org/jetbrains/kotlin/backend/konan/ObjCExportNameCollisionMode.kt new file mode 100644 index 00000000000..c1fe7888c1d --- /dev/null +++ b/native/base/src/main/kotlin/org/jetbrains/kotlin/backend/konan/ObjCExportNameCollisionMode.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2010-2022 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 + +enum class ObjCExportNameCollisionMode { + /* + * In this mode, exported ObjC symbol name collisions will not produce any compiler output. + */ + NONE, + + /* + * In this mode, exported ObjC symbol name collisions will produce a compiler warning. + */ + WARNING, + + /* + * In this mode, exported ObjC symbol name collisions will produce a compiler error. + */ + ERROR, + ; +} diff --git a/native/native.tests/testData/compilerOutput/ObjCExportDiagnostics/error.txt b/native/native.tests/testData/compilerOutput/ObjCExportDiagnostics/error.txt new file mode 100644 index 00000000000..92434f52fdb --- /dev/null +++ b/native/native.tests/testData/compilerOutput/ObjCExportDiagnostics/error.txt @@ -0,0 +1,25 @@ +error: name is mangled when generating Objective-C header + (at class ClassClashWithObjCName defined in pkg.lib1) +error: name is mangled when generating Objective-C header + (at enum entry one defined in pkg.lib1.E) +error: name is mangled when generating Objective-C header + (at enum entry TWO defined in pkg.lib1.E) +error: name is mangled when generating Objective-C header + (at fun values(): Array defined in pkg.lib1.E) +error: name is mangled when generating Objective-C header + (at val entries: EnumEntries defined in pkg.lib1.E) +error: name is mangled when generating Objective-C header + (at val prop: String defined in pkg.lib1.I2) +error: name is mangled when generating Objective-C header + (at fun ``(): String defined in pkg.lib1.I2) +error: name is mangled when generating Objective-C header + (at interface InterfaceClashWithObjCName defined in pkg.lib1) +error: name is mangled when generating Objective-C header + (at class ClassClash defined in pkg.lib2) +error: name is mangled when generating Objective-C header + (at interface InterfaceClash defined in pkg.lib2) +error: name "TOCEDLib1Kt" is mangled when generating Objective-C header +error: name "Lib1Kt" is mangled when generating Objective-C header +error: name is mangled when generating Objective-C header + (at fun topLevel(arg: Long): Unit defined in pkg.lib1) +COMPILATION_ERROR diff --git a/native/native.tests/tests/org/jetbrains/kotlin/konan/test/blackbox/CompilerOutputTest.kt b/native/native.tests/tests/org/jetbrains/kotlin/konan/test/blackbox/CompilerOutputTest.kt index b3e6d4e9ee0..f76bf47348f 100644 --- a/native/native.tests/tests/org/jetbrains/kotlin/konan/test/blackbox/CompilerOutputTest.kt +++ b/native/native.tests/tests/org/jetbrains/kotlin/konan/test/blackbox/CompilerOutputTest.kt @@ -26,6 +26,7 @@ import org.junit.jupiter.api.Assumptions import org.junit.jupiter.api.Tag import org.junit.jupiter.api.Test import java.io.File +import kotlin.test.assertIs abstract class CompilerOutputTestBase : AbstractNativeSimpleTest() { @Test @@ -83,30 +84,45 @@ abstract class CompilerOutputTestBase : AbstractNativeSimpleTest() { @Test fun testObjCExportDiagnostics() { + val rootDir = File("native/native.tests/testData/compilerOutput/ObjCExportDiagnostics") + val compilationResult = doBuildObjCFrameworkWithNameCollisions(rootDir, listOf("-Xbinary=objcExportReportNameCollisions=true")) + val goldenData = rootDir.resolve("output.txt") + + KotlinTestUtils.assertEqualsToFile(goldenData, compilationResult.toOutput()) + } + + @Test + fun testObjCExportDiagnosticsErrors() { + val rootDir = File("native/native.tests/testData/compilerOutput/ObjCExportDiagnostics") + val compilationResult = doBuildObjCFrameworkWithNameCollisions(rootDir, listOf("-Xbinary=objcExportErrorOnNameCollisions=true")) + assertIs(compilationResult) + val goldenData = rootDir.resolve("error.txt") + + KotlinTestUtils.assertEqualsToFile(goldenData, compilationResult.toOutput()) + } + + private fun doBuildObjCFrameworkWithNameCollisions(rootDir: File, additionalOptions: List): TestCompilationResult { Assumptions.assumeTrue(targets.hostTarget.family.isAppleFamily) - val rootDir = File("native/native.tests/testData/compilerOutput/ObjCExportDiagnostics") val settings = testRunSettings val lib1 = compileLibrary(settings, rootDir.resolve("lib1.kt")).assertSuccess().resultingArtifact val lib2 = compileLibrary(settings, rootDir.resolve("lib2.kt")).assertSuccess().resultingArtifact val freeCompilerArgs = TestCompilerArgs( - "-Xinclude=${lib1.path}", - "-Xinclude=${lib2.path}", - "-Xbinary=objcExportReportNameCollisions=true" + listOf( + "-Xinclude=${lib1.path}", + "-Xinclude=${lib2.path}" + ) + additionalOptions ) val expectedArtifact = TestCompilationArtifact.ObjCFramework(buildDir, "testObjCExportDiagnostics") - val compilationResult = ObjCFrameworkCompilation( + return ObjCFrameworkCompilation( settings, freeCompilerArgs, sourceModules = emptyList(), dependencies = emptyList(), expectedArtifact ).result - val goldenData = rootDir.resolve("output.txt") - - KotlinTestUtils.assertEqualsToFile(goldenData, compilationResult.toOutput()) } internal fun compileLibrary( diff --git a/native/objcexport-header-generator/impl/k1/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportNamer.kt b/native/objcexport-header-generator/impl/k1/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportNamer.kt index 0c5c06f5f25..90483e23cde 100644 --- a/native/objcexport-header-generator/impl/k1/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportNamer.kt +++ b/native/objcexport-header-generator/impl/k1/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportNamer.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.backend.konan.objcexport import org.jetbrains.kotlin.backend.common.serialization.findSourceFile import org.jetbrains.kotlin.backend.konan.InternalKotlinNativeApi import org.jetbrains.kotlin.backend.konan.KonanFqNames +import org.jetbrains.kotlin.backend.konan.ObjCExportNameCollisionMode import org.jetbrains.kotlin.backend.konan.UnitSuspendFunctionObjCExport import org.jetbrains.kotlin.backend.konan.cKeywords import org.jetbrains.kotlin.backend.konan.descriptors.isArray @@ -67,8 +68,8 @@ interface ObjCExportNamer { val ignoreInterfaceMethodCollisions: Boolean get() = false - val reportNameCollisions: Boolean - get() = false + val nameCollisionMode: ObjCExportNameCollisionMode + get() = ObjCExportNameCollisionMode.NONE } val topLevelNamePrefix: String @@ -308,7 +309,7 @@ class ObjCExportNamerImpl( objcGenerics: Boolean = false, disableSwiftMemberNameMangling: Boolean = false, ignoreInterfaceMethodCollisions: Boolean = false, - reportNameCollisions: Boolean = false, + nameCollisionMode: ObjCExportNameCollisionMode = ObjCExportNameCollisionMode.NONE, ) : this( object : ObjCExportNamer.Configuration { override val topLevelNamePrefix: String @@ -326,8 +327,8 @@ class ObjCExportNamerImpl( override val ignoreInterfaceMethodCollisions: Boolean get() = ignoreInterfaceMethodCollisions - override val reportNameCollisions: Boolean - get() = reportNameCollisions + override val nameCollisionMode: ObjCExportNameCollisionMode + get() = nameCollisionMode }, builtIns, mapper, @@ -863,13 +864,25 @@ class ObjCExportNamerImpl( return it } - if (configuration.reportNameCollisions && !reportedCollision) { - if (element is DeclarationDescriptor) { - problemCollector.reportWarning(element, "name is mangled when generating Objective-C header") - } else { - problemCollector.reportWarning("name \"$it\" is mangled when generating Objective-C header") - } + if (!reportedCollision) { reportedCollision = true + when (configuration.nameCollisionMode) { + ObjCExportNameCollisionMode.ERROR -> { + if (element is DeclarationDescriptor) { + problemCollector.reportError(element, "name is mangled when generating Objective-C header") + } else { + problemCollector.reportError("name \"$it\" is mangled when generating Objective-C header") + } + } + ObjCExportNameCollisionMode.WARNING -> { + if (element is DeclarationDescriptor) { + problemCollector.reportWarning(element, "name is mangled when generating Objective-C header") + } else { + problemCollector.reportWarning("name \"$it\" is mangled when generating Objective-C header") + } + } + ObjCExportNameCollisionMode.NONE -> Unit + } } } diff --git a/native/objcexport-header-generator/impl/k1/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportProblemCollector.kt b/native/objcexport-header-generator/impl/k1/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportProblemCollector.kt index 39dd47e88e8..c23672cc106 100644 --- a/native/objcexport-header-generator/impl/k1/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportProblemCollector.kt +++ b/native/objcexport-header-generator/impl/k1/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportProblemCollector.kt @@ -10,11 +10,15 @@ import org.jetbrains.kotlin.descriptors.DeclarationDescriptor interface ObjCExportProblemCollector { fun reportWarning(text: String) fun reportWarning(declaration: DeclarationDescriptor, text: String) + fun reportError(text: String) + fun reportError(declaration: DeclarationDescriptor, text: String) fun reportException(throwable: Throwable) object SILENT : ObjCExportProblemCollector { override fun reportWarning(text: String) {} override fun reportWarning(declaration: DeclarationDescriptor, text: String) {} + override fun reportError(text: String) {} + override fun reportError(declaration: DeclarationDescriptor, text: String) {} override fun reportException(throwable: Throwable) {} } } \ No newline at end of file