From 6e8a7d4662e92942fa4e9ae2c2df8e3b1de1e500 Mon Sep 17 00:00:00 2001 From: Pavel Kunyavskiy Date: Thu, 18 Jan 2024 14:56:57 +0100 Subject: [PATCH] [ObjCInterop] Implement @ObjCSignatureOverride This annotation leads to conflicting overloads error supression, in case where several function with the same argument types, but different argument names are inherited from ObjC class. We need to implement it in both K1 and K2 to make the IDE experience better. But the annotation itself wouldn't be available in K1. ^KT-61323 --- .../based/LLFirNativeTestGenerated.java | 18 +++ .../LLFirReversedNativeTestGenerated.java | 18 +++ .../diagnostics/FirNativeDiagnosticsList.kt | 4 + .../diagnostics/native/FirNativeErrors.kt | 2 + .../native/FirNativeErrorsDefaultMessages.kt | 11 ++ ...rNativeObjcOverrideApplicabilityChecker.kt | 74 +++++++++ .../checkers/NativeDeclarationCheckers.kt | 7 +- .../FirNonSuppressibleErrorNames.kt | 2 + .../FirConflictsDeclarationChecker.kt | 66 ++++++--- .../fir/session/FirNativeSessionFactory.kt | 9 +- .../backend/native/interop/FirObjCInterop.kt | 2 +- .../kotlin/resolve/OverloadResolver.kt | 38 ++++- .../cinterop/objc/overridabilityCondition.kt | 16 +- ...bjcOverrideConflictingOverloadsDisabled.kt | 131 ++++++++++++++++ ...ObjcOverrideConflictingOverloadsEnabled.kt | 140 ++++++++++++++++++ .../nativeTests/objcOverrideApplicability.kt | 22 +++ .../kotlin/config/LanguageVersionSettings.kt | 1 + .../kotlin/name/NativeStandardInteropNames.kt | 3 + .../kotlin/kotlinx/cinterop/Annotations.kt | 18 +++ .../kotlin/backend/konan/FirFrontend.kt | 3 - .../diagnostics/DefaultErrorMessagesNative.kt | 9 ++ .../resolve/konan/diagnostics/ErrorsNative.kt | 4 + .../NativeConflictingOverloadsDispatcher.kt | 50 +++++++ .../NativeObjCOverrideApplicabilityChecker.kt | 31 ++++ .../platform/NativePlatformConfigurator.kt | 2 + .../testData/interop/objc/smoke.kt | 2 +- .../testData/interop/objc/smoke_noopgc.kt | 2 +- .../DiagnosticsNativeTestGenerated.java | 18 +++ ...rontendNativeDiagnosticsTestGenerated.java | 18 +++ ...rontendNativeDiagnosticsTestGenerated.java | 18 +++ 30 files changed, 698 insertions(+), 41 deletions(-) create mode 100644 compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/FirNativeObjcOverrideApplicabilityChecker.kt create mode 100644 compiler/testData/diagnostics/nativeTests/noObjcOverrideConflictingOverloadsDisabled.kt create mode 100644 compiler/testData/diagnostics/nativeTests/noObjcOverrideConflictingOverloadsEnabled.kt create mode 100644 compiler/testData/diagnostics/nativeTests/objcOverrideApplicability.kt create mode 100644 native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/NativeConflictingOverloadsDispatcher.kt create mode 100644 native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/NativeObjCOverrideApplicabilityChecker.kt diff --git a/analysis/low-level-api-fir/low-level-api-fir-native/tests-gen/org/jetbrains/kotlin/analysis/low/level/api/fir/konan/compiler/based/LLFirNativeTestGenerated.java b/analysis/low-level-api-fir/low-level-api-fir-native/tests-gen/org/jetbrains/kotlin/analysis/low/level/api/fir/konan/compiler/based/LLFirNativeTestGenerated.java index f114770a8be..fc533ca5b5c 100644 --- a/analysis/low-level-api-fir/low-level-api-fir-native/tests-gen/org/jetbrains/kotlin/analysis/low/level/api/fir/konan/compiler/based/LLFirNativeTestGenerated.java +++ b/analysis/low-level-api-fir/low-level-api-fir-native/tests-gen/org/jetbrains/kotlin/analysis/low/level/api/fir/konan/compiler/based/LLFirNativeTestGenerated.java @@ -68,6 +68,18 @@ public class LLFirNativeTestGenerated extends AbstractLLFirNativeTest { runTest("compiler/testData/diagnostics/nativeTests/nativeProtectedFunCall.kt"); } + @Test + @TestMetadata("noObjcOverrideConflictingOverloadsDisabled.kt") + public void testNoObjcOverrideConflictingOverloadsDisabled() throws Exception { + runTest("compiler/testData/diagnostics/nativeTests/noObjcOverrideConflictingOverloadsDisabled.kt"); + } + + @Test + @TestMetadata("noObjcOverrideConflictingOverloadsEnabled.kt") + public void testNoObjcOverrideConflictingOverloadsEnabled() throws Exception { + runTest("compiler/testData/diagnostics/nativeTests/noObjcOverrideConflictingOverloadsEnabled.kt"); + } + @Test @TestMetadata("objCName.kt") public void testObjCName() throws Exception { @@ -104,6 +116,12 @@ public class LLFirNativeTestGenerated extends AbstractLLFirNativeTest { runTest("compiler/testData/diagnostics/nativeTests/objCRefinement.kt"); } + @Test + @TestMetadata("objcOverrideApplicability.kt") + public void testObjcOverrideApplicability() throws Exception { + runTest("compiler/testData/diagnostics/nativeTests/objcOverrideApplicability.kt"); + } + @Test @TestMetadata("resolveToDelegatedProperty.kt") public void testResolveToDelegatedProperty() throws Exception { diff --git a/analysis/low-level-api-fir/low-level-api-fir-native/tests-gen/org/jetbrains/kotlin/analysis/low/level/api/fir/konan/compiler/based/LLFirReversedNativeTestGenerated.java b/analysis/low-level-api-fir/low-level-api-fir-native/tests-gen/org/jetbrains/kotlin/analysis/low/level/api/fir/konan/compiler/based/LLFirReversedNativeTestGenerated.java index e0bed3dc3dd..580e10d418e 100644 --- a/analysis/low-level-api-fir/low-level-api-fir-native/tests-gen/org/jetbrains/kotlin/analysis/low/level/api/fir/konan/compiler/based/LLFirReversedNativeTestGenerated.java +++ b/analysis/low-level-api-fir/low-level-api-fir-native/tests-gen/org/jetbrains/kotlin/analysis/low/level/api/fir/konan/compiler/based/LLFirReversedNativeTestGenerated.java @@ -68,6 +68,18 @@ public class LLFirReversedNativeTestGenerated extends AbstractLLFirReversedNativ runTest("compiler/testData/diagnostics/nativeTests/nativeProtectedFunCall.kt"); } + @Test + @TestMetadata("noObjcOverrideConflictingOverloadsDisabled.kt") + public void testNoObjcOverrideConflictingOverloadsDisabled() throws Exception { + runTest("compiler/testData/diagnostics/nativeTests/noObjcOverrideConflictingOverloadsDisabled.kt"); + } + + @Test + @TestMetadata("noObjcOverrideConflictingOverloadsEnabled.kt") + public void testNoObjcOverrideConflictingOverloadsEnabled() throws Exception { + runTest("compiler/testData/diagnostics/nativeTests/noObjcOverrideConflictingOverloadsEnabled.kt"); + } + @Test @TestMetadata("objCName.kt") public void testObjCName() throws Exception { @@ -104,6 +116,12 @@ public class LLFirReversedNativeTestGenerated extends AbstractLLFirReversedNativ runTest("compiler/testData/diagnostics/nativeTests/objCRefinement.kt"); } + @Test + @TestMetadata("objcOverrideApplicability.kt") + public void testObjcOverrideApplicability() throws Exception { + runTest("compiler/testData/diagnostics/nativeTests/objcOverrideApplicability.kt"); + } + @Test @TestMetadata("resolveToDelegatedProperty.kt") public void testResolveToDelegatedProperty() throws Exception { diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirNativeDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirNativeDiagnosticsList.kt index d8ef9c1076d..b1a908560c2 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirNativeDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirNativeDiagnosticsList.kt @@ -103,5 +103,9 @@ object NATIVE_DIAGNOSTICS_LIST : DiagnosticList("FirNativeErrors") { val CONSTRUCTOR_MATCHES_SEVERAL_SUPER_CONSTRUCTORS by error() { parameter("annotation") } + val CONFLICTING_OBJC_OVERLOADS by error() { + parameter>("conflictingOverloads") + } + val INAPPLICABLE_OBJC_OVERRIDE by error() } } diff --git a/compiler/fir/checkers/checkers.native/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/native/FirNativeErrors.kt b/compiler/fir/checkers/checkers.native/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/native/FirNativeErrors.kt index 2e4f0c38dcd..52edf5c5798 100644 --- a/compiler/fir/checkers/checkers.native/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/native/FirNativeErrors.kt +++ b/compiler/fir/checkers/checkers.native/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/native/FirNativeErrors.kt @@ -62,6 +62,8 @@ object FirNativeErrors { val CONSTRUCTOR_OVERRIDES_ALREADY_OVERRIDDEN_OBJC_INITIALIZER: KtDiagnosticFactory1 by error1() val CONSTRUCTOR_DOES_NOT_OVERRIDE_ANY_SUPER_CONSTRUCTOR: KtDiagnosticFactory1 by error1() val CONSTRUCTOR_MATCHES_SEVERAL_SUPER_CONSTRUCTORS: KtDiagnosticFactory1 by error1() + val CONFLICTING_OBJC_OVERLOADS: KtDiagnosticFactory1>> by error1>>() + val INAPPLICABLE_OBJC_OVERRIDE: KtDiagnosticFactory0 by error0() init { RootDiagnosticRendererFactory.registerFactory(FirNativeErrorsDefaultMessages) diff --git a/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/diagnostics/native/FirNativeErrorsDefaultMessages.kt b/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/diagnostics/native/FirNativeErrorsDefaultMessages.kt index 0522c143401..0a7a9b4f303 100644 --- a/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/diagnostics/native/FirNativeErrorsDefaultMessages.kt +++ b/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/diagnostics/native/FirNativeErrorsDefaultMessages.kt @@ -12,6 +12,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.SYMBOL import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.SYMBOLS import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.CANNOT_CHECK_FOR_FORWARD_DECLARATION +import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.CONFLICTING_OBJC_OVERLOADS import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.CONSTRUCTOR_DOES_NOT_OVERRIDE_ANY_SUPER_CONSTRUCTOR import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.CONSTRUCTOR_MATCHES_SEVERAL_SUPER_CONSTRUCTORS import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.CONSTRUCTOR_OVERRIDES_ALREADY_OVERRIDDEN_OBJC_INITIALIZER @@ -20,6 +21,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.FORW import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.FORWARD_DECLARATION_AS_REIFIED_TYPE_ARGUMENT import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.INAPPLICABLE_EXACT_OBJC_NAME import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.INAPPLICABLE_OBJC_NAME +import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.INAPPLICABLE_OBJC_OVERRIDE import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.INAPPLICABLE_SHARED_IMMUTABLE_PROPERTY import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.INAPPLICABLE_SHARED_IMMUTABLE_TOP_LEVEL import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.INAPPLICABLE_THREAD_LOCAL @@ -145,5 +147,14 @@ object FirNativeErrorsDefaultMessages : BaseDiagnosticRendererFactory() { "Constructor with ''@{0}'' matches more than one of super constructors.", TO_STRING ) + map.put( + CONFLICTING_OBJC_OVERLOADS, + "Conflicting overloads: {0}. Add @ObjCSignatureOverride to allow collision for functions inherited from Objective-C.", + SYMBOLS + ) + map.put( + INAPPLICABLE_OBJC_OVERRIDE, + "@ObjCSignatureOverride is only allowed on methods overriding methods from Objective-C.", + ) } } diff --git a/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/FirNativeObjcOverrideApplicabilityChecker.kt b/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/FirNativeObjcOverrideApplicabilityChecker.kt new file mode 100644 index 00000000000..65361ffdd2e --- /dev/null +++ b/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/FirNativeObjcOverrideApplicabilityChecker.kt @@ -0,0 +1,74 @@ +/* + * 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.fir.analysis.native.checkers + +import org.jetbrains.kotlin.config.LanguageFeature +import org.jetbrains.kotlin.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.diagnostics.KtDiagnosticFactory1 +import org.jetbrains.kotlin.diagnostics.reportOn +import org.jetbrains.kotlin.fir.analysis.checkers.MppCheckerKind +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirFunctionChecker +import org.jetbrains.kotlin.fir.analysis.checkers.declaration.PlatformConflictDeclarationsDiagnosticDispatcher +import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors +import org.jetbrains.kotlin.fir.backend.native.interop.getObjCMethodInfoFromOverriddenFunctions +import org.jetbrains.kotlin.fir.declarations.* +import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol +import org.jetbrains.kotlin.name.NativeStandardInteropNames.Annotations.objCSignatureOverrideClassId +import org.jetbrains.kotlin.utils.SmartSet + +private fun FirFunctionSymbol<*>.isInheritedFromObjc(context: CheckerContext): Boolean { + return getObjCMethodInfoFromOverriddenFunctions(context.session, context.scopeSession) != null +} + +/** + * This function basically checks that these two functions have different objective-C signature. + * + * This signature consists of function name and parameter names except first. + * + * So we ignore the first parameter name, but check others + */ +private fun FirFunctionSymbol<*>.hasDifferentParameterNames(other: FirFunctionSymbol<*>) : Boolean { + return valueParameterSymbols.drop(1).map { it.name } != other.valueParameterSymbols.drop(1).map { it.name } +} + +object NativeConflictDeclarationsDiagnosticDispatcher : PlatformConflictDeclarationsDiagnosticDispatcher { + override fun getDiagnostic( + conflictingDeclaration: FirBasedSymbol<*>, + symbols: SmartSet>, + context: CheckerContext + ): KtDiagnosticFactory1>>? { + if (context.languageVersionSettings.supportsFeature(LanguageFeature.ObjCSignatureOverrideAnnotation)) { + if (conflictingDeclaration is FirFunctionSymbol<*> && symbols.all { it is FirFunctionSymbol<*> }) { + if (conflictingDeclaration.isInheritedFromObjc(context) && symbols.all { (it as FirFunctionSymbol<*>).isInheritedFromObjc(context) }) { + if (symbols.all { (it as FirFunctionSymbol<*>).hasDifferentParameterNames(conflictingDeclaration) }) { + if (conflictingDeclaration.hasAnnotation(objCSignatureOverrideClassId, context.session)) { + return null + } else { + return FirNativeErrors.CONFLICTING_OBJC_OVERLOADS + } + } + } + } + } + return PlatformConflictDeclarationsDiagnosticDispatcher.DEFAULT.getDiagnostic(conflictingDeclaration, symbols, context) + } +} + +object FirNativeObjcOverrideApplicabilityChecker : FirFunctionChecker(MppCheckerKind.Platform) { + override fun check( + declaration: FirFunction, + context: CheckerContext, + reporter: DiagnosticReporter, + ) { + if (declaration.hasAnnotation(objCSignatureOverrideClassId, context.session)) { + if (!declaration.symbol.isInheritedFromObjc(context)) { + reporter.reportOn(declaration.getAnnotationByClassId(objCSignatureOverrideClassId, context.session)?.source, FirNativeErrors.INAPPLICABLE_OBJC_OVERRIDE, context) + } + } + } +} \ No newline at end of file diff --git a/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/NativeDeclarationCheckers.kt b/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/NativeDeclarationCheckers.kt index 18a4f9dece9..079e203d43f 100644 --- a/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/NativeDeclarationCheckers.kt +++ b/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/NativeDeclarationCheckers.kt @@ -15,7 +15,12 @@ object NativeDeclarationCheckers : DeclarationCheckers() { FirNativeSharedImmutableChecker, FirNativeThreadLocalChecker, FirNativeIdentifierChecker, - FirNativeObjCNameChecker + FirNativeObjCNameChecker, + ) + + override val functionCheckers: Set + get() = setOf( + FirNativeObjcOverrideApplicabilityChecker ) override val callableDeclarationCheckers: Set diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirNonSuppressibleErrorNames.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirNonSuppressibleErrorNames.kt index 66af294e8a2..0cb2cf9b13e 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirNonSuppressibleErrorNames.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirNonSuppressibleErrorNames.kt @@ -679,6 +679,8 @@ val FIR_NON_SUPPRESSIBLE_ERROR_NAMES: Set = setOf( "CONSTRUCTOR_OVERRIDES_ALREADY_OVERRIDDEN_OBJC_INITIALIZER", "CONSTRUCTOR_DOES_NOT_OVERRIDE_ANY_SUPER_CONSTRUCTOR", "CONSTRUCTOR_MATCHES_SEVERAL_SUPER_CONSTRUCTORS", + "CONFLICTING_OBJC_OVERLOADS", + "INAPPLICABLE_OBJC_OVERRIDE", "NESTED_EXTERNAL_DECLARATION", "WRONG_EXTERNAL_DECLARATION", "NESTED_CLASS_IN_EXTERNAL_INTERFACE", diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirConflictsDeclarationChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirConflictsDeclarationChecker.kt index 7329e2b3d2a..b7d2de6ed5c 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirConflictsDeclarationChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirConflictsDeclarationChecker.kt @@ -6,9 +6,13 @@ package org.jetbrains.kotlin.fir.analysis.checkers.declaration import org.jetbrains.kotlin.KtFakeSourceElementKind +import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.diagnostics.KtDiagnosticFactory1 import org.jetbrains.kotlin.diagnostics.reportOn import org.jetbrains.kotlin.fir.FirNameConflictsTrackerComponent +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.FirSessionComponent import org.jetbrains.kotlin.fir.analysis.checkers.* import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors @@ -18,14 +22,43 @@ import org.jetbrains.kotlin.fir.scopes.impl.FirPackageMemberScope import org.jetbrains.kotlin.fir.scopes.impl.PACKAGE_MEMBER import org.jetbrains.kotlin.fir.scopes.impl.typeAliasForConstructor import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol +import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.SpecialNames import org.jetbrains.kotlin.utils.SmartSet +interface PlatformConflictDeclarationsDiagnosticDispatcher : FirSessionComponent { + fun getDiagnostic( + conflictingDeclaration: FirBasedSymbol<*>, + symbols: SmartSet>, + context: CheckerContext + ): KtDiagnosticFactory1>>? + + object DEFAULT : PlatformConflictDeclarationsDiagnosticDispatcher { + override fun getDiagnostic( + conflictingDeclaration: FirBasedSymbol<*>, + symbols: SmartSet>, + context: CheckerContext + ): KtDiagnosticFactory1>> { + return when { + conflictingDeclaration is FirNamedFunctionSymbol || conflictingDeclaration is FirConstructorSymbol -> { + FirErrors.CONFLICTING_OVERLOADS + } + conflictingDeclaration is FirClassLikeSymbol<*> && + conflictingDeclaration.getContainingClassSymbol(context.session) == null && + symbols.any { it is FirClassLikeSymbol<*> } -> { + FirErrors.PACKAGE_OR_CLASSIFIER_REDECLARATION + } + else -> { + FirErrors.REDECLARATION + } + } + } + } +} + +val FirSession.conflictDeclarationsDiagnosticDispatcher: PlatformConflictDeclarationsDiagnosticDispatcher? by FirSession.nullableSessionComponentAccessor() + object FirConflictsDeclarationChecker : FirBasicDeclarationChecker(MppCheckerKind.Platform) { override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) { when (declaration) { @@ -90,21 +123,16 @@ object FirConflictsDeclarationChecker : FirBasicDeclarationChecker(MppCheckerKin conflictingDeclaration.isPrimaryConstructor && symbols.all { it.isPrimaryConstructor } ) return@forEach - when { - symbols.singleOrNull()?.let { isExpectAndActual(conflictingDeclaration, it) } == true -> { - reporter.reportOn(source, FirErrors.EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE, conflictingDeclaration, context) - } - conflictingDeclaration is FirNamedFunctionSymbol || conflictingDeclaration is FirConstructorSymbol -> { - reporter.reportOn(source, FirErrors.CONFLICTING_OVERLOADS, symbols, context) - } - conflictingDeclaration is FirClassLikeSymbol<*> && - conflictingDeclaration.getContainingClassSymbol(context.session) == null && - symbols.any { it is FirClassLikeSymbol<*> } -> { - reporter.reportOn(source, FirErrors.PACKAGE_OR_CLASSIFIER_REDECLARATION, symbols, context) - } - else -> { - reporter.reportOn(source, FirErrors.REDECLARATION, symbols, context) - } + if (symbols.singleOrNull()?.let { isExpectAndActual(conflictingDeclaration, it) } == true) { + reporter.reportOn(source, FirErrors.EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE, conflictingDeclaration, context) + return@forEach + } + + val dispatcher = context.session.conflictDeclarationsDiagnosticDispatcher ?: PlatformConflictDeclarationsDiagnosticDispatcher.DEFAULT + val factory = dispatcher.getDiagnostic(conflictingDeclaration, symbols, context) + + if (factory != null) { + reporter.reportOn(source, factory, symbols, context) } } } diff --git a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/FirNativeSessionFactory.kt b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/FirNativeSessionFactory.kt index 5185404104c..9ae4603140f 100644 --- a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/FirNativeSessionFactory.kt +++ b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/FirNativeSessionFactory.kt @@ -7,23 +7,25 @@ package org.jetbrains.kotlin.fir.session import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.fir.* +import org.jetbrains.kotlin.fir.analysis.checkers.declaration.PlatformConflictDeclarationsDiagnosticDispatcher import org.jetbrains.kotlin.fir.analysis.native.checkers.FirNativeCastChecker +import org.jetbrains.kotlin.fir.analysis.native.checkers.NativeConflictDeclarationsDiagnosticDispatcher import org.jetbrains.kotlin.fir.backend.native.FirNativeClassMapper +import org.jetbrains.kotlin.fir.backend.native.FirNativeOverrideChecker import org.jetbrains.kotlin.fir.checkers.registerNativeCheckers import org.jetbrains.kotlin.fir.deserialization.ModuleDataProvider import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar import org.jetbrains.kotlin.fir.java.FirProjectSessionProvider import org.jetbrains.kotlin.fir.resolve.providers.impl.FirBuiltinSyntheticFunctionInterfaceProvider import org.jetbrains.kotlin.fir.scopes.FirKotlinScopeProvider +import org.jetbrains.kotlin.fir.scopes.FirOverrideChecker import org.jetbrains.kotlin.fir.scopes.FirPlatformClassMapper import org.jetbrains.kotlin.fir.session.FirSessionFactoryHelper.registerDefaultComponents -import org.jetbrains.kotlin.library.isNativeStdlib import org.jetbrains.kotlin.library.metadata.impl.KlibResolvedModuleDescriptorsFactoryImpl.Companion.FORWARD_DECLARATIONS_MODULE_NAME import org.jetbrains.kotlin.library.metadata.resolver.KotlinResolvedLibrary import org.jetbrains.kotlin.name.Name object FirNativeSessionFactory : FirAbstractSessionFactory() { - @OptIn(SessionConfiguration::class) fun createLibrarySession( mainModuleName: Name, resolvedLibraries: List, @@ -63,7 +65,6 @@ object FirNativeSessionFactory : FirAbstractSessionFactory() { }) } - @OptIn(SessionConfiguration::class) fun createModuleBasedSession( moduleData: FirModuleData, sessionProvider: FirProjectSessionProvider, @@ -102,5 +103,7 @@ object FirNativeSessionFactory : FirAbstractSessionFactory() { fun FirSession.registerNativeComponents() { register(FirPlatformClassMapper::class, FirNativeClassMapper()) register(FirPlatformSpecificCastChecker::class, FirNativeCastChecker) + register(PlatformConflictDeclarationsDiagnosticDispatcher::class, NativeConflictDeclarationsDiagnosticDispatcher) + register(FirOverrideChecker::class, FirNativeOverrideChecker(this)) } } diff --git a/compiler/fir/native/src/org/jetbrains/kotlin/fir/backend/native/interop/FirObjCInterop.kt b/compiler/fir/native/src/org/jetbrains/kotlin/fir/backend/native/interop/FirObjCInterop.kt index e109da7e12d..5872c028ec0 100644 --- a/compiler/fir/native/src/org/jetbrains/kotlin/fir/backend/native/interop/FirObjCInterop.kt +++ b/compiler/fir/native/src/org/jetbrains/kotlin/fir/backend/native/interop/FirObjCInterop.kt @@ -23,7 +23,7 @@ import org.jetbrains.kotlin.name.NativeStandardInteropNames import org.jetbrains.kotlin.native.interop.ObjCMethodInfo -internal fun FirFunctionSymbol<*>.getObjCMethodInfoFromOverriddenFunctions(session: FirSession, scopeSession: ScopeSession): ObjCMethodInfo? { +fun FirFunctionSymbol<*>.getObjCMethodInfoFromOverriddenFunctions(session: FirSession, scopeSession: ScopeSession): ObjCMethodInfo? { decodeObjCMethodAnnotation(session)?.let { return it } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/OverloadResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/OverloadResolver.kt index fa1f75b3b62..1d56c9111e6 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/OverloadResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/OverloadResolver.kt @@ -16,9 +16,12 @@ package org.jetbrains.kotlin.resolve +import com.intellij.psi.PsiElement import com.intellij.util.containers.MultiMap import org.jetbrains.kotlin.config.LanguageVersionSettings +import org.jetbrains.kotlin.container.DefaultImplementation import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1 import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.diagnostics.reportOnDeclaration import org.jetbrains.kotlin.idea.MainFunctionDetector @@ -30,11 +33,35 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.platform import org.jetbrains.kotlin.resolve.scopes.MemberScope import java.util.* +@DefaultImplementation(impl = ConflictingOverloadsDispatcher.Default::class) +interface ConflictingOverloadsDispatcher { + fun getDiagnostic( + languageVersionSettings: LanguageVersionSettings, + declaration: DeclarationDescriptor, + redeclarations: Collection + ): DiagnosticFactory1>? + + object Default : ConflictingOverloadsDispatcher { + override fun getDiagnostic( + languageVersionSettings: LanguageVersionSettings, + declaration: DeclarationDescriptor, + redeclarations: Collection + ): DiagnosticFactory1>? { + return when (declaration) { + is PropertyDescriptor, is ClassifierDescriptor -> Errors.REDECLARATION + is FunctionDescriptor -> Errors.CONFLICTING_OVERLOADS + else -> null + } + } + } +} + class OverloadResolver( private val trace: BindingTrace, private val overloadFilter: OverloadFilter, private val overloadChecker: OverloadChecker, - languageVersionSettings: LanguageVersionSettings, + private val errorDispatcher: ConflictingOverloadsDispatcher, + private val languageVersionSettings: LanguageVersionSettings, mainFunctionDetectorFactory: MainFunctionDetector.Factory ) { @@ -299,12 +326,9 @@ class OverloadResolver( if (redeclarations.isEmpty()) return for (memberDescriptor in redeclarations) { - when (memberDescriptor) { - is PropertyDescriptor, - is ClassifierDescriptor -> - reportOnDeclaration(trace, memberDescriptor) { Errors.REDECLARATION.on(it, redeclarations) } - is FunctionDescriptor -> - reportOnDeclaration(trace, memberDescriptor) { Errors.CONFLICTING_OVERLOADS.on(it, redeclarations) } + val diagnostic = errorDispatcher.getDiagnostic(languageVersionSettings, memberDescriptor, redeclarations) ?: continue + reportOnDeclaration(trace, memberDescriptor) { + diagnostic.on(it, redeclarations) } } } diff --git a/compiler/testData/codegen/box/cinterop/objc/overridabilityCondition.kt b/compiler/testData/codegen/box/cinterop/objc/overridabilityCondition.kt index 68a289d39ed..e5ffd29e9e4 100644 --- a/compiler/testData/codegen/box/cinterop/objc/overridabilityCondition.kt +++ b/compiler/testData/codegen/box/cinterop/objc/overridabilityCondition.kt @@ -42,28 +42,34 @@ headerFilter = lib.h // FILE: main.kt @file:OptIn(kotlinx.cinterop.ExperimentalForeignApi::class) -import lib.ObjCClass +// required for K1 +@file:Suppress("CONFLICTING_OVERLOADS", "UNRESOLVED_REFERENCE", "API_NOT_AVAILABLE") + +import lib.ObjCClass +import kotlinx.cinterop.ObjCSignatureOverride -@Suppress("CONFLICTING_OVERLOADS") class OverrideAll : ObjCClass() { + @ObjCSignatureOverride override fun fooWithArg(arg: Int, arg2: String?) = "D" + @ObjCSignatureOverride override fun fooWithArg(ohNoOtherName: Int, name2: String?) = "E" + @ObjCSignatureOverride override fun fooWithArg(arg: Int, name3: String?) = "F" } -@Suppress("CONFLICTING_OVERLOADS") class OverrideNone : ObjCClass() { } -@Suppress("CONFLICTING_OVERLOADS") class OverrideOne : ObjCClass() { override fun fooWithArg(arg: Int, arg2: String?) = "G" } -@Suppress("CONFLICTING_OVERLOADS") class OverrideWithDifferentFirstArgName : ObjCClass() { + @ObjCSignatureOverride override fun fooWithArg(a: Int, arg2: String?) = "H" + @ObjCSignatureOverride override fun fooWithArg(b: Int, name2: String?) = "I" + @ObjCSignatureOverride override fun fooWithArg(c: Int, name3: String?) = "J" } diff --git a/compiler/testData/diagnostics/nativeTests/noObjcOverrideConflictingOverloadsDisabled.kt b/compiler/testData/diagnostics/nativeTests/noObjcOverrideConflictingOverloadsDisabled.kt new file mode 100644 index 00000000000..6e35b3a1d5c --- /dev/null +++ b/compiler/testData/diagnostics/nativeTests/noObjcOverrideConflictingOverloadsDisabled.kt @@ -0,0 +1,131 @@ +// !LANGUAGE: -ObjCSignatureOverrideAnnotation +// FIR_IDENTICAL +// WITH_PLATFORM_LIBS + +import kotlinx.cinterop.* +import platform.darwin.* +import platform.Foundation.* +import platform.CoreFoundation.* +import platform.CoreBluetooth.* + +class Delelegate1 : CBCentralManagerDelegateProtocol, NSObject() { + override fun centralManager(central: CBCentralManager, willRestoreState: Map): Unit = TODO() + override fun centralManager( + central: CBCentralManager, + didDiscoverPeripheral: CBPeripheral, + advertisementData: Map, + RSSI: NSNumber + ): Unit = TODO() + + override fun centralManager(central: CBCentralManager, didConnectPeripheral: CBPeripheral): Unit = TODO() + override fun centralManager(central: CBCentralManager, didFailToConnectPeripheral: CBPeripheral, error: NSError?): Unit = TODO() + override fun centralManager(central: CBCentralManager, didDisconnectPeripheral: CBPeripheral, error: NSError?): Unit = TODO() + + override fun centralManager( + central: CBCentralManager, + didDisconnectPeripheral: CBPeripheral, + timestamp: Double, + isReconnecting: Boolean, + error: NSError? + ): Unit = TODO() + + override fun centralManagerDidUpdateState(central: CBCentralManager): Unit = TODO() +} + +class Delegate2 : CBCentralManagerDelegateProtocol, NSObject() { + override fun centralManager(central: CBCentralManager, willRestoreState: Map): Unit = TODO() + override fun centralManager( + central: CBCentralManager, + didDiscoverPeripheral: CBPeripheral, + advertisementData: Map, + RSSI: NSNumber + ): Unit = TODO() + + override fun centralManager(central: CBCentralManager, didConnectPeripheral: CBPeripheral): Unit = TODO() + override fun centralManager( + central: CBCentralManager, + didDisconnectPeripheral: CBPeripheral, + timestamp: Double, + isReconnecting: Boolean, + error: NSError? + ): Unit = TODO() + + override fun centralManagerDidUpdateState(central: CBCentralManager): Unit = TODO() + + override fun centralManager(central: CBCentralManager, didFailToConnectPeripheral: CBPeripheral, error: NSError?): Unit = TODO() + override fun centralManager(central: CBCentralManager, didDisconnectPeripheral: CBPeripheral, error: NSError?): Unit = TODO() +} + +class Delegate3 : CBCentralManagerDelegateProtocol, NSObject() { + override fun centralManager(central: CBCentralManager, willRestoreState: Map): Unit = TODO() + override fun centralManager( + central: CBCentralManager, + didDiscoverPeripheral: CBPeripheral, + advertisementData: Map, + RSSI: NSNumber + ): Unit = TODO() + + override fun centralManager(central: CBCentralManager, didConnectPeripheral: CBPeripheral): Unit = TODO() + override fun centralManager( + central: CBCentralManager, + didDisconnectPeripheral: CBPeripheral, + timestamp: Double, + isReconnecting: Boolean, + error: NSError? + ): Unit = TODO() + + override fun centralManagerDidUpdateState(central: CBCentralManager): Unit = TODO() + + override fun centralManager(central: CBCentralManager, didFailToConnectPeripheral: CBPeripheral, error: NSError?): Unit = TODO() + override fun centralManager(central: CBCentralManager, didDisconnectPeripheral: CBPeripheral, error: NSError?): Unit = TODO() +} + +class Delegate4 : CBCentralManagerDelegateProtocol, NSObject() { + override fun centralManager(central: CBCentralManager, willRestoreState: Map): Unit = TODO() + override fun centralManager( + central: CBCentralManager, + didDiscoverPeripheral: CBPeripheral, + advertisementData: Map, + RSSI: NSNumber + ): Unit = TODO() + + override fun centralManager(central: CBCentralManager, didConnectPeripheral: CBPeripheral): Unit = TODO() + override fun centralManager( + central: CBCentralManager, + didDisconnectPeripheral: CBPeripheral, + timestamp: Double, + isReconnecting: Boolean, + error: NSError? + ): Unit = TODO() + + override fun centralManagerDidUpdateState(central: CBCentralManager): Unit = TODO() + + override fun centralManager(central: CBCentralManager, didFailToConnectPeripheral: CBPeripheral, error: NSError?): Unit = TODO() + override fun centralManager(central: CBCentralManager, didDisconnectPeripheral: CBPeripheral, error: NSError?): Unit = TODO() +} + +class Delegate5 : CBCentralManagerDelegateProtocol, NSObject() { + override fun centralManager(central: CBCentralManager, willRestoreState: Map): Unit = TODO() + override fun centralManager( + central: CBCentralManager, + didDiscoverPeripheral: CBPeripheral, + advertisementData: Map, + RSSI: NSNumber + ): Unit = TODO() + + override fun centralManager(central: CBCentralManager, didConnectPeripheral: CBPeripheral): Unit = TODO() + override fun centralManager( + central: CBCentralManager, + didDisconnectPeripheral: CBPeripheral, + timestamp: Double, + isReconnecting: Boolean, + error: NSError? + ): Unit = TODO() + + override fun centralManagerDidUpdateState(central: CBCentralManager): Unit = TODO() + + override fun centralManager(central: CBCentralManager, didFailToConnectPeripheral: CBPeripheral, error: NSError?): Unit = TODO() + override fun centralManager(central: CBCentralManager, didFailToConnectPeripheral: CBPeripheral, error: NSError?): Unit = TODO() + override fun centralManager(central: CBCentralManager, didDisconnectPeripheral: CBPeripheral, error: NSError?): Unit = TODO() +} + diff --git a/compiler/testData/diagnostics/nativeTests/noObjcOverrideConflictingOverloadsEnabled.kt b/compiler/testData/diagnostics/nativeTests/noObjcOverrideConflictingOverloadsEnabled.kt new file mode 100644 index 00000000000..0948a3891f5 --- /dev/null +++ b/compiler/testData/diagnostics/nativeTests/noObjcOverrideConflictingOverloadsEnabled.kt @@ -0,0 +1,140 @@ +// !LANGUAGE: +ObjCSignatureOverrideAnnotation +// !API_VERSION: 2.0 +// FIR_IDENTICAL +// WITH_PLATFORM_LIBS + +import kotlinx.cinterop.* +import platform.darwin.* +import platform.Foundation.* +import platform.CoreFoundation.* +import platform.CoreBluetooth.* + +class Delelegate1 : CBCentralManagerDelegateProtocol, NSObject() { + override fun centralManager(central: CBCentralManager, willRestoreState: Map): Unit = TODO() + override fun centralManager( + central: CBCentralManager, + didDiscoverPeripheral: CBPeripheral, + advertisementData: Map, + RSSI: NSNumber + ): Unit = TODO() + + override fun centralManager(central: CBCentralManager, didConnectPeripheral: CBPeripheral): Unit = TODO() + @ObjCSignatureOverride + override fun centralManager(central: CBCentralManager, didFailToConnectPeripheral: CBPeripheral, error: NSError?): Unit = TODO() + @ObjCSignatureOverride + override fun centralManager(central: CBCentralManager, didDisconnectPeripheral: CBPeripheral, error: NSError?): Unit = TODO() + + override fun centralManager( + central: CBCentralManager, + didDisconnectPeripheral: CBPeripheral, + timestamp: Double, + isReconnecting: Boolean, + error: NSError? + ): Unit = TODO() + + override fun centralManagerDidUpdateState(central: CBCentralManager): Unit = TODO() +} + +class Delegate2 : CBCentralManagerDelegateProtocol, NSObject() { + override fun centralManager(central: CBCentralManager, willRestoreState: Map): Unit = TODO() + override fun centralManager( + central: CBCentralManager, + didDiscoverPeripheral: CBPeripheral, + advertisementData: Map, + RSSI: NSNumber + ): Unit = TODO() + + override fun centralManager(central: CBCentralManager, didConnectPeripheral: CBPeripheral): Unit = TODO() + override fun centralManager( + central: CBCentralManager, + didDisconnectPeripheral: CBPeripheral, + timestamp: Double, + isReconnecting: Boolean, + error: NSError? + ): Unit = TODO() + + override fun centralManagerDidUpdateState(central: CBCentralManager): Unit = TODO() + + override fun centralManager(central: CBCentralManager, didFailToConnectPeripheral: CBPeripheral, error: NSError?): Unit = TODO() + override fun centralManager(central: CBCentralManager, didDisconnectPeripheral: CBPeripheral, error: NSError?): Unit = TODO() +} + +class Delegate3 : CBCentralManagerDelegateProtocol, NSObject() { + override fun centralManager(central: CBCentralManager, willRestoreState: Map): Unit = TODO() + override fun centralManager( + central: CBCentralManager, + didDiscoverPeripheral: CBPeripheral, + advertisementData: Map, + RSSI: NSNumber + ): Unit = TODO() + + override fun centralManager(central: CBCentralManager, didConnectPeripheral: CBPeripheral): Unit = TODO() + override fun centralManager( + central: CBCentralManager, + didDisconnectPeripheral: CBPeripheral, + timestamp: Double, + isReconnecting: Boolean, + error: NSError? + ): Unit = TODO() + + override fun centralManagerDidUpdateState(central: CBCentralManager): Unit = TODO() + + @ObjCSignatureOverride + override fun centralManager(central: CBCentralManager, didFailToConnectPeripheral: CBPeripheral, error: NSError?): Unit = TODO() + override fun centralManager(central: CBCentralManager, didDisconnectPeripheral: CBPeripheral, error: NSError?): Unit = TODO() +} + +class Delegate4 : CBCentralManagerDelegateProtocol, NSObject() { + override fun centralManager(central: CBCentralManager, willRestoreState: Map): Unit = TODO() + override fun centralManager( + central: CBCentralManager, + didDiscoverPeripheral: CBPeripheral, + advertisementData: Map, + RSSI: NSNumber + ): Unit = TODO() + + override fun centralManager(central: CBCentralManager, didConnectPeripheral: CBPeripheral): Unit = TODO() + override fun centralManager( + central: CBCentralManager, + didDisconnectPeripheral: CBPeripheral, + timestamp: Double, + isReconnecting: Boolean, + error: NSError? + ): Unit = TODO() + + override fun centralManagerDidUpdateState(central: CBCentralManager): Unit = TODO() + + @ObjCSignatureOverride + override fun centralManager(central: CBCentralManager, didFailToConnectPeripheral: CBPeripheral, error: NSError?): Unit = TODO() + @ObjCSignatureOverride + override fun centralManager(central: CBCentralManager, didDisconnectPeripheral: CBPeripheral, error: NSError?): Unit = TODO() +} + +class Delegate5 : CBCentralManagerDelegateProtocol, NSObject() { + override fun centralManager(central: CBCentralManager, willRestoreState: Map): Unit = TODO() + override fun centralManager( + central: CBCentralManager, + didDiscoverPeripheral: CBPeripheral, + advertisementData: Map, + RSSI: NSNumber + ): Unit = TODO() + + override fun centralManager(central: CBCentralManager, didConnectPeripheral: CBPeripheral): Unit = TODO() + override fun centralManager( + central: CBCentralManager, + didDisconnectPeripheral: CBPeripheral, + timestamp: Double, + isReconnecting: Boolean, + error: NSError? + ): Unit = TODO() + + override fun centralManagerDidUpdateState(central: CBCentralManager): Unit = TODO() + + @ObjCSignatureOverride + override fun centralManager(central: CBCentralManager, didFailToConnectPeripheral: CBPeripheral, error: NSError?): Unit = TODO() + @ObjCSignatureOverride + override fun centralManager(central: CBCentralManager, didFailToConnectPeripheral: CBPeripheral, error: NSError?): Unit = TODO() + @ObjCSignatureOverride + override fun centralManager(central: CBCentralManager, didDisconnectPeripheral: CBPeripheral, error: NSError?): Unit = TODO() +} + diff --git a/compiler/testData/diagnostics/nativeTests/objcOverrideApplicability.kt b/compiler/testData/diagnostics/nativeTests/objcOverrideApplicability.kt new file mode 100644 index 00000000000..4b27b55fc2c --- /dev/null +++ b/compiler/testData/diagnostics/nativeTests/objcOverrideApplicability.kt @@ -0,0 +1,22 @@ +// !API_VERSION: 2.0 +// FIR_IDENTICAL +// WITH_PLATFORM_LIBS + +import kotlinx.cinterop.* +import platform.darwin.* + +@ObjCSignatureOverride +fun foo() = 1 + +class A { + @ObjCSignatureOverride + fun foo() = 1 +} + +@ObjCSignatureOverride +class B : NSObject() { + @ObjCSignatureOverride + fun foo() = 1 + @ObjCSignatureOverride + val v = 1 +} diff --git a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt index e02c2186233..64b32346d5a 100644 --- a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt +++ b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt @@ -310,6 +310,7 @@ enum class LanguageFeature( DisableCompatibilityModeForNewInference(KOTLIN_2_0, kind = OTHER), // KT-63558 (umbrella), KT-64306, KT-64307, KT-64308 DfaBooleanVariables(KOTLIN_2_0), // KT-25747 LightweightLambdas(KOTLIN_2_0, kind = OTHER), // KT-45375 + ObjCSignatureOverrideAnnotation(KOTLIN_2_0, sinceApiVersion = ApiVersion.KOTLIN_2_0, kind = OTHER), // KT-61323 // 2.1 diff --git a/core/compiler.common.native/src/org/jetbrains/kotlin/name/NativeStandardInteropNames.kt b/core/compiler.common.native/src/org/jetbrains/kotlin/name/NativeStandardInteropNames.kt index 5f59b84067c..34accf4b8e9 100644 --- a/core/compiler.common.native/src/org/jetbrains/kotlin/name/NativeStandardInteropNames.kt +++ b/core/compiler.common.native/src/org/jetbrains/kotlin/name/NativeStandardInteropNames.kt @@ -25,6 +25,9 @@ object NativeStandardInteropNames { val objCOutletClassId = ClassId(cInteropPackage, Name.identifier("ObjCOutlet")) val objCOverrideInitClassId = ClassId(cInteropPackage, Name.identifier("ObjCObjectBase.OverrideInit")) + object Annotations { + val objCSignatureOverrideClassId = ClassId(cInteropPackage, Name.identifier("ObjCSignatureOverride")) + } object ForwardDeclarations { private val cNamesPackage = FqName("cnames") diff --git a/kotlin-native/Interop/Runtime/src/main/kotlin/kotlinx/cinterop/Annotations.kt b/kotlin-native/Interop/Runtime/src/main/kotlin/kotlinx/cinterop/Annotations.kt index 13a5f252d19..869b08ddc7a 100644 --- a/kotlin-native/Interop/Runtime/src/main/kotlin/kotlinx/cinterop/Annotations.kt +++ b/kotlin-native/Interop/Runtime/src/main/kotlin/kotlinx/cinterop/Annotations.kt @@ -63,3 +63,21 @@ public annotation class BetaInteropApi @Retention(AnnotationRetention.BINARY) @RequiresOptIn(level = RequiresOptIn.Level.ERROR) public annotation class ExperimentalForeignApi + +/** + * Marks functions for which Objective-C rules should be used for determinating whether two functions are conflicting. + * + * Objective-C allows overloads by selector. Such functions can have same types, and differ only by parameter names in Kotlin, + * while it is not generally supported. + * + * This leads to an error, when a Kotlin class attempts to override both versions of a method that is overloaded by names only. + * + * If all such methods are annotated by this annotation, the error would be suppressed. + * + * Annotation is only applicable to overrides of methods coming from Objective-C interop. + */ +@Target(AnnotationTarget.FUNCTION) +@Retention(AnnotationRetention.SOURCE) +@Suppress("NEWER_VERSION_IN_SINCE_KOTLIN") +@SinceKotlin("2.0") +public annotation class ObjCSignatureOverride \ No newline at end of file diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/FirFrontend.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/FirFrontend.kt index ba5931b75c4..bef07c1a1f8 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/FirFrontend.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/FirFrontend.kt @@ -68,9 +68,6 @@ internal inline fun PhaseContext.firFrontend( metadataCompilationMode = config.metadataKlib, isCommonSource = isCommonSource, fileBelongsToModule = fileBelongsToModule, - registerExtraComponents = { - it.register(FirOverrideChecker::class, FirNativeOverrideChecker(it)) - }, ) val outputs = sessionsWithSources.map { (session, sources) -> diff --git a/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/DefaultErrorMessagesNative.kt b/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/DefaultErrorMessagesNative.kt index b951b62bbda..8ea7873be69 100644 --- a/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/DefaultErrorMessagesNative.kt +++ b/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/DefaultErrorMessagesNative.kt @@ -90,6 +90,15 @@ private val DIAGNOSTIC_FACTORY_TO_RENDERER by lazy { "Can't refer to forward declaration ''{0}'' from class literal", Renderers.RENDER_TYPE ) + put( + ErrorsNative.CONFLICTING_OBJC_OVERLOADS, + "Conflicting overloads: {0}. Add @ObjCSignatureOverride to allow collision for functions inherited from Objective-C.", + CommonRenderers.commaSeparated(Renderers.FQ_NAMES_IN_TYPES) + ) + put( + ErrorsNative.INAPPLICABLE_OBJC_OVERRIDE, + "@ObjCSignatureOverride is only allowed on functions inherited from Objective-C.", + ) } } diff --git a/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/ErrorsNative.kt b/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/ErrorsNative.kt index 28c1beacf17..83618388f34 100644 --- a/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/ErrorsNative.kt +++ b/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/ErrorsNative.kt @@ -69,6 +69,10 @@ object ErrorsNative { val FORWARD_DECLARATION_AS_REIFIED_TYPE_ARGUMENT = DiagnosticFactory1.create(Severity.ERROR) @JvmField val FORWARD_DECLARATION_AS_CLASS_LITERAL = DiagnosticFactory1.create(Severity.ERROR) + @JvmField + val CONFLICTING_OBJC_OVERLOADS = DiagnosticFactory1.create>(Severity.ERROR) + @JvmField + val INAPPLICABLE_OBJC_OVERRIDE = DiagnosticFactory0.create(Severity.ERROR) init { Errors.Initializer.initializeFactoryNames(ErrorsNative::class.java) diff --git a/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/NativeConflictingOverloadsDispatcher.kt b/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/NativeConflictingOverloadsDispatcher.kt new file mode 100644 index 00000000000..c67797a10f7 --- /dev/null +++ b/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/NativeConflictingOverloadsDispatcher.kt @@ -0,0 +1,50 @@ +/* + * 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.resolve.konan.diagnostics + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.config.LanguageFeature +import org.jetbrains.kotlin.config.LanguageVersionSettings +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1 +import org.jetbrains.kotlin.ir.objcinterop.getObjCMethodInfo +import org.jetbrains.kotlin.name.NativeStandardInteropNames.Annotations.objCSignatureOverrideClassId +import org.jetbrains.kotlin.resolve.ConflictingOverloadsDispatcher + +/** + * This function basically checks that these two functions have different objective-C signature. + * + * This signature consists of function name and parameter names except first. + * + * So we ignore the first parameter name, but check others + */ +private fun FunctionDescriptor.hasDifferentParameterNames(other: FunctionDescriptor) : Boolean { + return valueParameters.drop(1).map { it.name } != other.valueParameters.drop(1).map { it.name } +} + +object NativeConflictingOverloadsDispatcher : ConflictingOverloadsDispatcher { + override fun getDiagnostic( + languageVersionSettings: LanguageVersionSettings, + declaration: DeclarationDescriptor, + redeclarations: Collection + ): DiagnosticFactory1>? { + if (languageVersionSettings.supportsFeature(LanguageFeature.ObjCSignatureOverrideAnnotation)) { + if (declaration is FunctionDescriptor && redeclarations.all { it is FunctionDescriptor }) { + if (declaration.getObjCMethodInfo() != null && redeclarations.all { (it as FunctionDescriptor).getObjCMethodInfo() != null }) { + if (redeclarations.all { it === declaration || (it as FunctionDescriptor).hasDifferentParameterNames(declaration) }) { + if (declaration.annotations.hasAnnotation(objCSignatureOverrideClassId.asSingleFqName())) { + return null + } else { + return ErrorsNative.CONFLICTING_OBJC_OVERLOADS + } + } + } + } + } + return ConflictingOverloadsDispatcher.Default.getDiagnostic(languageVersionSettings, declaration, redeclarations) + } +} \ No newline at end of file diff --git a/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/NativeObjCOverrideApplicabilityChecker.kt b/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/NativeObjCOverrideApplicabilityChecker.kt new file mode 100644 index 00000000000..826e9d6e1e6 --- /dev/null +++ b/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/NativeObjCOverrideApplicabilityChecker.kt @@ -0,0 +1,31 @@ +/* + * 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.resolve.konan.diagnostics + +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.ir.objcinterop.getObjCMethodInfo +import org.jetbrains.kotlin.name.NativeStandardInteropNames.Annotations.objCSignatureOverrideClassId +import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils +import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker +import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext + + +object NativeObjcOverrideApplicabilityChecker : DeclarationChecker { + override fun check( + declaration: KtDeclaration, + descriptor: DeclarationDescriptor, + context: DeclarationCheckerContext, + ) { + if (descriptor is FunctionDescriptor) { + val annotation = descriptor.annotations.findAnnotation(objCSignatureOverrideClassId.asSingleFqName()) ?: return + if (descriptor.getObjCMethodInfo() == null) { + context.trace.report(ErrorsNative.INAPPLICABLE_OBJC_OVERRIDE.on(DescriptorToSourceUtils.getSourceFromAnnotation(annotation) ?: declaration)) + } + } + } +} \ No newline at end of file diff --git a/native/frontend/src/org/jetbrains/kotlin/resolve/konan/platform/NativePlatformConfigurator.kt b/native/frontend/src/org/jetbrains/kotlin/resolve/konan/platform/NativePlatformConfigurator.kt index f070c6f903a..32d03b4bd8c 100644 --- a/native/frontend/src/org/jetbrains/kotlin/resolve/konan/platform/NativePlatformConfigurator.kt +++ b/native/frontend/src/org/jetbrains/kotlin/resolve/konan/platform/NativePlatformConfigurator.kt @@ -30,6 +30,7 @@ object NativePlatformConfigurator : PlatformConfiguratorBase( NativeObjCNameChecker, NativeObjCNameOverridesChecker, NativeObjCRefinementChecker, NativeObjCRefinementAnnotationChecker, NativeObjCRefinementOverridesChecker, NativeHiddenFromObjCInheritanceChecker, + NativeObjcOverrideApplicabilityChecker, ), platformSpecificCastChecker = NativePlatformSpecificCastChecker ) { @@ -37,6 +38,7 @@ object NativePlatformConfigurator : PlatformConfiguratorBase( container.useInstance(NativeInliningRule) container.useImpl() container.useImpl() + container.useInstance(NativeConflictingOverloadsDispatcher) } override fun configureModuleDependentCheckers(container: StorageComponentContainer) { diff --git a/native/native.tests/testData/interop/objc/smoke.kt b/native/native.tests/testData/interop/objc/smoke.kt index 3d9fb5e5c91..953fe0f2458 100644 --- a/native/native.tests/testData/interop/objc/smoke.kt +++ b/native/native.tests/testData/interop/objc/smoke.kt @@ -119,7 +119,7 @@ class Bar : Foo() { } } -@Suppress("CONFLICTING_OVERLOADS") +@Suppress("CONFLICTING_OBJC_OVERLOADS") class MutablePairImpl(first: Int, second: Int) : NSObject(), MutablePairProtocol { private var elements = intArrayOf(first, second) diff --git a/native/native.tests/testData/interop/objc/smoke_noopgc.kt b/native/native.tests/testData/interop/objc/smoke_noopgc.kt index 3d9fb5e5c91..953fe0f2458 100644 --- a/native/native.tests/testData/interop/objc/smoke_noopgc.kt +++ b/native/native.tests/testData/interop/objc/smoke_noopgc.kt @@ -119,7 +119,7 @@ class Bar : Foo() { } } -@Suppress("CONFLICTING_OVERLOADS") +@Suppress("CONFLICTING_OBJC_OVERLOADS") class MutablePairImpl(first: Int, second: Int) : NSObject(), MutablePairProtocol { private var elements = intArrayOf(first, second) diff --git a/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/test/diagnostics/DiagnosticsNativeTestGenerated.java b/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/test/diagnostics/DiagnosticsNativeTestGenerated.java index 773be1533c4..dc1d58f20da 100644 --- a/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/test/diagnostics/DiagnosticsNativeTestGenerated.java +++ b/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/test/diagnostics/DiagnosticsNativeTestGenerated.java @@ -66,6 +66,18 @@ public class DiagnosticsNativeTestGenerated extends AbstractDiagnosticsNativeTes runTest("compiler/testData/diagnostics/nativeTests/nativeProtectedFunCall.kt"); } + @Test + @TestMetadata("noObjcOverrideConflictingOverloadsDisabled.kt") + public void testNoObjcOverrideConflictingOverloadsDisabled() throws Exception { + runTest("compiler/testData/diagnostics/nativeTests/noObjcOverrideConflictingOverloadsDisabled.kt"); + } + + @Test + @TestMetadata("noObjcOverrideConflictingOverloadsEnabled.kt") + public void testNoObjcOverrideConflictingOverloadsEnabled() throws Exception { + runTest("compiler/testData/diagnostics/nativeTests/noObjcOverrideConflictingOverloadsEnabled.kt"); + } + @Test @TestMetadata("objCName.kt") public void testObjCName() throws Exception { @@ -102,6 +114,12 @@ public class DiagnosticsNativeTestGenerated extends AbstractDiagnosticsNativeTes runTest("compiler/testData/diagnostics/nativeTests/objCRefinement.kt"); } + @Test + @TestMetadata("objcOverrideApplicability.kt") + public void testObjcOverrideApplicability() throws Exception { + runTest("compiler/testData/diagnostics/nativeTests/objcOverrideApplicability.kt"); + } + @Test @TestMetadata("resolveToDelegatedProperty.kt") public void testResolveToDelegatedProperty() throws Exception { diff --git a/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/test/diagnostics/FirLightTreeOldFrontendNativeDiagnosticsTestGenerated.java b/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/test/diagnostics/FirLightTreeOldFrontendNativeDiagnosticsTestGenerated.java index b401a5a771c..6e2a384ffa8 100644 --- a/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/test/diagnostics/FirLightTreeOldFrontendNativeDiagnosticsTestGenerated.java +++ b/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/test/diagnostics/FirLightTreeOldFrontendNativeDiagnosticsTestGenerated.java @@ -70,6 +70,18 @@ public class FirLightTreeOldFrontendNativeDiagnosticsTestGenerated extends Abstr runTest("compiler/testData/diagnostics/nativeTests/nativeProtectedFunCall.kt"); } + @Test + @TestMetadata("noObjcOverrideConflictingOverloadsDisabled.kt") + public void testNoObjcOverrideConflictingOverloadsDisabled() throws Exception { + runTest("compiler/testData/diagnostics/nativeTests/noObjcOverrideConflictingOverloadsDisabled.kt"); + } + + @Test + @TestMetadata("noObjcOverrideConflictingOverloadsEnabled.kt") + public void testNoObjcOverrideConflictingOverloadsEnabled() throws Exception { + runTest("compiler/testData/diagnostics/nativeTests/noObjcOverrideConflictingOverloadsEnabled.kt"); + } + @Test @TestMetadata("objCName.kt") public void testObjCName() throws Exception { @@ -106,6 +118,12 @@ public class FirLightTreeOldFrontendNativeDiagnosticsTestGenerated extends Abstr runTest("compiler/testData/diagnostics/nativeTests/objCRefinement.kt"); } + @Test + @TestMetadata("objcOverrideApplicability.kt") + public void testObjcOverrideApplicability() throws Exception { + runTest("compiler/testData/diagnostics/nativeTests/objcOverrideApplicability.kt"); + } + @Test @TestMetadata("resolveToDelegatedProperty.kt") public void testResolveToDelegatedProperty() throws Exception { diff --git a/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/test/diagnostics/FirPsiOldFrontendNativeDiagnosticsTestGenerated.java b/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/test/diagnostics/FirPsiOldFrontendNativeDiagnosticsTestGenerated.java index 58565265b3e..fa22bbb762f 100644 --- a/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/test/diagnostics/FirPsiOldFrontendNativeDiagnosticsTestGenerated.java +++ b/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/test/diagnostics/FirPsiOldFrontendNativeDiagnosticsTestGenerated.java @@ -70,6 +70,18 @@ public class FirPsiOldFrontendNativeDiagnosticsTestGenerated extends AbstractFir runTest("compiler/testData/diagnostics/nativeTests/nativeProtectedFunCall.kt"); } + @Test + @TestMetadata("noObjcOverrideConflictingOverloadsDisabled.kt") + public void testNoObjcOverrideConflictingOverloadsDisabled() throws Exception { + runTest("compiler/testData/diagnostics/nativeTests/noObjcOverrideConflictingOverloadsDisabled.kt"); + } + + @Test + @TestMetadata("noObjcOverrideConflictingOverloadsEnabled.kt") + public void testNoObjcOverrideConflictingOverloadsEnabled() throws Exception { + runTest("compiler/testData/diagnostics/nativeTests/noObjcOverrideConflictingOverloadsEnabled.kt"); + } + @Test @TestMetadata("objCName.kt") public void testObjCName() throws Exception { @@ -106,6 +118,12 @@ public class FirPsiOldFrontendNativeDiagnosticsTestGenerated extends AbstractFir runTest("compiler/testData/diagnostics/nativeTests/objCRefinement.kt"); } + @Test + @TestMetadata("objcOverrideApplicability.kt") + public void testObjcOverrideApplicability() throws Exception { + runTest("compiler/testData/diagnostics/nativeTests/objcOverrideApplicability.kt"); + } + @Test @TestMetadata("resolveToDelegatedProperty.kt") public void testResolveToDelegatedProperty() throws Exception {