[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
This commit is contained in:
Pavel Kunyavskiy
2024-01-18 14:56:57 +01:00
committed by Space Team
parent 77eee0d3fc
commit 6e8a7d4662
30 changed files with 698 additions and 41 deletions
@@ -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.",
)
}
}
@@ -69,6 +69,10 @@ object ErrorsNative {
val FORWARD_DECLARATION_AS_REIFIED_TYPE_ARGUMENT = DiagnosticFactory1.create<PsiElement, KotlinType>(Severity.ERROR)
@JvmField
val FORWARD_DECLARATION_AS_CLASS_LITERAL = DiagnosticFactory1.create<PsiElement, KotlinType>(Severity.ERROR)
@JvmField
val CONFLICTING_OBJC_OVERLOADS = DiagnosticFactory1.create<PsiElement, Collection<DeclarationDescriptor>>(Severity.ERROR)
@JvmField
val INAPPLICABLE_OBJC_OVERRIDE = DiagnosticFactory0.create<PsiElement>(Severity.ERROR)
init {
Errors.Initializer.initializeFactoryNames(ErrorsNative::class.java)
@@ -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<DeclarationDescriptor>
): DiagnosticFactory1<PsiElement, Collection<DeclarationDescriptor>>? {
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)
}
}
@@ -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))
}
}
}
}
@@ -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<NativeIdentifierChecker>()
container.useImpl<NativeForwardDeclarationRttiChecker>()
container.useInstance(NativeConflictingOverloadsDispatcher)
}
override fun configureModuleDependentCheckers(container: StorageComponentContainer) {