[FE 1.0] Add NativeObjCRefinement checkers

This commit is contained in:
Rick Clephas
2022-08-19 16:46:15 +02:00
committed by SvyatoslavScherbina
parent 6ae517a2df
commit 203af3afdd
9 changed files with 545 additions and 4 deletions
@@ -0,0 +1,131 @@
// FILE: kotlin.kt
package kotlin.native
@Target(AnnotationTarget.ANNOTATION_CLASS)
@Retention(AnnotationRetention.BINARY)
@MustBeDocumented
annotation class HidesFromObjC
@HidesFromObjC
@Target(AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.BINARY)
annotation class HiddenFromObjC
@Target(AnnotationTarget.ANNOTATION_CLASS)
@Retention(AnnotationRetention.BINARY)
annotation class RefinesInSwift
@RefinesInSwift
@Target(AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.BINARY)
public annotation class ShouldRefineInSwift
// FILE: plugin.kt
package plugin
@HidesFromObjC
@Target(AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.BINARY)
annotation class PluginHiddenFromObjC
@RefinesInSwift
@Target(AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.BINARY)
annotation class PluginShouldRefineInSwift
// FILE: test.kt
import plugin.PluginHiddenFromObjC
import plugin.PluginShouldRefineInSwift
@HidesFromObjC
<!REDUNDANT_SWIFT_REFINEMENT!>@RefinesInSwift<!>
@Target(AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.BINARY)
annotation class MyRefinedAnnotationA
<!INVALID_OBJC_REFINEMENT_TARGETS!>@HidesFromObjC<!>
@Target(AnnotationTarget.PROPERTY, AnnotationTarget.CLASS)
@Retention(AnnotationRetention.BINARY)
annotation class MyRefinedAnnotationB
<!INVALID_OBJC_REFINEMENT_TARGETS!>@RefinesInSwift<!>
@Retention(AnnotationRetention.BINARY)
annotation class MyRefinedAnnotationC
@RefinesInSwift
@Target(AnnotationTarget.PROPERTY)
@Retention(AnnotationRetention.BINARY)
annotation class MyRefinedAnnotationD
@HiddenFromObjC
<!REDUNDANT_SWIFT_REFINEMENT!>@ShouldRefineInSwift<!>
var refinedProperty: Int = 0
@PluginHiddenFromObjC
<!REDUNDANT_SWIFT_REFINEMENT!>@PluginShouldRefineInSwift<!>
fun pluginRefinedFunction() { }
@HiddenFromObjC
@PluginHiddenFromObjC
fun multipleObjCRefinementsFunction() { }
@ShouldRefineInSwift
@PluginShouldRefineInSwift
fun multipleSwiftRefinementsFunction() { }
@HiddenFromObjC
@PluginHiddenFromObjC
<!REDUNDANT_SWIFT_REFINEMENT!>@ShouldRefineInSwift<!>
<!REDUNDANT_SWIFT_REFINEMENT!>@PluginShouldRefineInSwift<!>
fun multipleMixedRefinementsFunction() { }
interface InterfaceA {
val barA: Int
val barB: Int
fun fooA()
@HiddenFromObjC
fun fooB()
}
interface InterfaceB {
val barA: Int
@ShouldRefineInSwift
val barB: Int
@HiddenFromObjC
fun fooA()
@HiddenFromObjC
fun fooB()
}
open class ClassA: InterfaceA, InterfaceB {
<!INCOMPATIBLE_OBJC_REFINEMENT_OVERRIDE!>@HiddenFromObjC<!>
override val barA: Int = 0
<!INCOMPATIBLE_OBJC_REFINEMENT_OVERRIDE!>@ShouldRefineInSwift<!>
override val barB: Int = 0
<!INCOMPATIBLE_OBJC_REFINEMENT_OVERRIDE!>override fun fooA() { }<!>
override fun fooB() { }
@HiddenFromObjC
open fun fooC() { }
}
class ClassB: ClassA() {
@HiddenFromObjC
override fun fooB() { }
<!INCOMPATIBLE_OBJC_REFINEMENT_OVERRIDE!>@ShouldRefineInSwift<!>
override fun fooC() { }
}
open class Base {
@HiddenFromObjC
open fun foo() {}
}
interface I {
fun foo()
}
<!INCOMPATIBLE_OBJC_REFINEMENT_OVERRIDE!>open class Derived : Base(), I<!>
open class Derived2 : Derived() {
override fun foo() {}
}
@@ -0,0 +1,161 @@
package
@kotlin.native.HiddenFromObjC @kotlin.native.ShouldRefineInSwift public var refinedProperty: kotlin.Int
@kotlin.native.HiddenFromObjC @plugin.PluginHiddenFromObjC @kotlin.native.ShouldRefineInSwift @plugin.PluginShouldRefineInSwift public fun multipleMixedRefinementsFunction(): kotlin.Unit
@kotlin.native.HiddenFromObjC @plugin.PluginHiddenFromObjC public fun multipleObjCRefinementsFunction(): kotlin.Unit
@kotlin.native.ShouldRefineInSwift @plugin.PluginShouldRefineInSwift public fun multipleSwiftRefinementsFunction(): kotlin.Unit
@plugin.PluginHiddenFromObjC @plugin.PluginShouldRefineInSwift public fun pluginRefinedFunction(): kotlin.Unit
public open class Base {
public constructor Base()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
@kotlin.native.HiddenFromObjC public open fun foo(): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public open class ClassA : InterfaceA, InterfaceB {
public constructor ClassA()
@kotlin.native.HiddenFromObjC public open override /*2*/ val barA: kotlin.Int = 0
@kotlin.native.ShouldRefineInSwift public open override /*2*/ val barB: kotlin.Int = 0
public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*2*/ fun fooA(): kotlin.Unit
public open override /*2*/ fun fooB(): kotlin.Unit
@kotlin.native.HiddenFromObjC public open fun fooC(): kotlin.Unit
public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String
}
public final class ClassB : ClassA {
public constructor ClassB()
@kotlin.native.HiddenFromObjC public open override /*1*/ /*fake_override*/ val barA: kotlin.Int
@kotlin.native.ShouldRefineInSwift public open override /*1*/ /*fake_override*/ val barB: kotlin.Int
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun fooA(): kotlin.Unit
@kotlin.native.HiddenFromObjC public open override /*1*/ fun fooB(): kotlin.Unit
@kotlin.native.ShouldRefineInSwift public open override /*1*/ fun fooC(): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public open class Derived : Base, I {
public constructor Derived()
public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
@kotlin.native.HiddenFromObjC public open override /*2*/ /*fake_override*/ fun foo(): kotlin.Unit
public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String
}
public open class Derived2 : Derived {
public constructor Derived2()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ fun foo(): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface I {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public abstract fun foo(): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface InterfaceA {
public abstract val barA: kotlin.Int
public abstract val barB: kotlin.Int
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public abstract fun fooA(): kotlin.Unit
@kotlin.native.HiddenFromObjC public abstract fun fooB(): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface InterfaceB {
public abstract val barA: kotlin.Int
@kotlin.native.ShouldRefineInSwift public abstract val barB: kotlin.Int
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
@kotlin.native.HiddenFromObjC public abstract fun fooA(): kotlin.Unit
@kotlin.native.HiddenFromObjC public abstract fun fooB(): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@kotlin.native.HidesFromObjC @kotlin.native.RefinesInSwift @kotlin.annotation.Target(allowedTargets = {AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION}) @kotlin.annotation.Retention(value = AnnotationRetention.BINARY) public final annotation class MyRefinedAnnotationA : kotlin.Annotation {
public constructor MyRefinedAnnotationA()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@kotlin.native.HidesFromObjC @kotlin.annotation.Target(allowedTargets = {AnnotationTarget.PROPERTY, AnnotationTarget.CLASS}) @kotlin.annotation.Retention(value = AnnotationRetention.BINARY) public final annotation class MyRefinedAnnotationB : kotlin.Annotation {
public constructor MyRefinedAnnotationB()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@kotlin.native.RefinesInSwift @kotlin.annotation.Retention(value = AnnotationRetention.BINARY) public final annotation class MyRefinedAnnotationC : kotlin.Annotation {
public constructor MyRefinedAnnotationC()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@kotlin.native.RefinesInSwift @kotlin.annotation.Target(allowedTargets = {AnnotationTarget.PROPERTY}) @kotlin.annotation.Retention(value = AnnotationRetention.BINARY) public final annotation class MyRefinedAnnotationD : kotlin.Annotation {
public constructor MyRefinedAnnotationD()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
package kotlin {
package kotlin.native {
@kotlin.native.HidesFromObjC @kotlin.annotation.Target(allowedTargets = {AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION}) @kotlin.annotation.Retention(value = AnnotationRetention.BINARY) public final annotation class HiddenFromObjC : kotlin.Annotation {
public constructor HiddenFromObjC()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.ANNOTATION_CLASS}) @kotlin.annotation.Retention(value = AnnotationRetention.BINARY) @kotlin.annotation.MustBeDocumented public final annotation class HidesFromObjC : kotlin.Annotation {
public constructor HidesFromObjC()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.ANNOTATION_CLASS}) @kotlin.annotation.Retention(value = AnnotationRetention.BINARY) public final annotation class RefinesInSwift : kotlin.Annotation {
public constructor RefinesInSwift()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@kotlin.native.RefinesInSwift @kotlin.annotation.Target(allowedTargets = {AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION}) @kotlin.annotation.Retention(value = AnnotationRetention.BINARY) public final annotation class ShouldRefineInSwift : kotlin.Annotation {
public constructor ShouldRefineInSwift()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
}
}
package plugin {
@kotlin.native.HidesFromObjC @kotlin.annotation.Target(allowedTargets = {AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION}) @kotlin.annotation.Retention(value = AnnotationRetention.BINARY) public final annotation class PluginHiddenFromObjC : kotlin.Annotation {
public constructor PluginHiddenFromObjC()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@kotlin.native.RefinesInSwift @kotlin.annotation.Target(allowedTargets = {AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION}) @kotlin.annotation.Retention(value = AnnotationRetention.BINARY) public final annotation class PluginShouldRefineInSwift : kotlin.Annotation {
public constructor PluginShouldRefineInSwift()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
}
@@ -36,6 +36,12 @@ public class DiagnosticsNativeTestGenerated extends AbstractDiagnosticsNativeTes
runTest("compiler/testData/diagnostics/nativeTests/objCName.kt");
}
@Test
@TestMetadata("objCRefinement.kt")
public void testObjCRefinement() throws Exception {
runTest("compiler/testData/diagnostics/nativeTests/objCRefinement.kt");
}
@Test
@TestMetadata("sharedImmutable.kt")
public void testSharedImmutable() throws Exception {
@@ -54,6 +54,17 @@ private val DIAGNOSTIC_FACTORY_TO_RENDERER by lazy {
put(ErrorsNative.INAPPLICABLE_EXACT_OBJC_NAME, "Exact @ObjCName is only applicable to classes, objects and interfaces")
put(ErrorsNative.MISSING_EXACT_OBJC_NAME, "Exact @ObjCName is required to have an ObjC name")
put(ErrorsNative.NON_LITERAL_OBJC_NAME_ARG, "@ObjCName accepts only literal string and boolean values")
put(ErrorsNative.REDUNDANT_SWIFT_REFINEMENT, "An ObjC refined declaration can't also be refined in Swift")
put(
ErrorsNative.INCOMPATIBLE_OBJC_REFINEMENT_OVERRIDE,
"Refined declaration \"{0}\" overrides declarations with different or no refinement from {1}",
Renderers.NAME,
CommonRenderers.commaSeparated(Renderers.NAME)
)
put(
ErrorsNative.INVALID_OBJC_REFINEMENT_TARGETS,
"Refines annotations are only applicable to annotations with targets FUNCTION and/or PROPERTY"
)
}
}
@@ -52,8 +52,14 @@ object ErrorsNative {
val MISSING_EXACT_OBJC_NAME = DiagnosticFactory0.create<KtElement>(Severity.ERROR)
@JvmField
val NON_LITERAL_OBJC_NAME_ARG = DiagnosticFactory0.create<KtElement>(Severity.ERROR)
@JvmField
val REDUNDANT_SWIFT_REFINEMENT = DiagnosticFactory0.create<KtElement>(Severity.ERROR)
@JvmField
val INCOMPATIBLE_OBJC_REFINEMENT_OVERRIDE = DiagnosticFactory2.create<KtElement, DeclarationDescriptor, Collection<DeclarationDescriptor>>(Severity.ERROR)
@JvmField
val INVALID_OBJC_REFINEMENT_TARGETS = DiagnosticFactory0.create<KtElement>(Severity.ERROR)
init {
Errors.Initializer.initializeFactoryNames(ErrorsNative::class.java)
}
}
}
@@ -0,0 +1,62 @@
/*
* 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.resolve.konan.diagnostics
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.resolve.AnnotationChecker
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker
import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext
import org.jetbrains.kotlin.resolve.konan.diagnostics.NativeObjCRefinementChecker.hidesFromObjCFqName
import org.jetbrains.kotlin.resolve.konan.diagnostics.NativeObjCRefinementChecker.refinesInSwiftFqName
object NativeObjCRefinementAnnotationChecker : DeclarationChecker {
private val supportedTargets = arrayOf(KotlinTarget.FUNCTION, KotlinTarget.PROPERTY)
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
if (descriptor !is ClassDescriptor || descriptor.kind != ClassKind.ANNOTATION_CLASS) return
val (objCAnnotation, swiftAnnotation) = descriptor.findRefinesAnnotations()
if (objCAnnotation == null && swiftAnnotation == null) return
if (objCAnnotation != null && swiftAnnotation != null) {
val reportLocation = DescriptorToSourceUtils.getSourceFromAnnotation(swiftAnnotation) ?: declaration
context.trace.report(ErrorsNative.REDUNDANT_SWIFT_REFINEMENT.on(reportLocation))
}
val targets = AnnotationChecker.applicableTargetSet(descriptor)
val unsupportedTargets = targets - supportedTargets
if (unsupportedTargets.isNotEmpty()) {
objCAnnotation?.let { context.trace.reportInvalidAnnotationTargets(declaration, it) }
swiftAnnotation?.let { context.trace.reportInvalidAnnotationTargets(declaration, it) }
}
}
private fun DeclarationDescriptor.findRefinesAnnotations(): Pair<AnnotationDescriptor?, AnnotationDescriptor?> {
var objCAnnotation: AnnotationDescriptor? = null
var swiftAnnotation: AnnotationDescriptor? = null
for (annotation in annotations) {
when (annotation.fqName) {
hidesFromObjCFqName -> objCAnnotation = annotation
refinesInSwiftFqName -> swiftAnnotation = annotation
}
if (objCAnnotation != null && swiftAnnotation != null) break
}
return objCAnnotation to swiftAnnotation
}
private fun BindingTrace.reportInvalidAnnotationTargets(
declaration: KtDeclaration,
annotation: AnnotationDescriptor
) {
val reportLocation = DescriptorToSourceUtils.getSourceFromAnnotation(annotation) ?: declaration
report(ErrorsNative.INVALID_OBJC_REFINEMENT_TARGETS.on(reportLocation))
}
}
@@ -0,0 +1,57 @@
/*
* 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.resolve.konan.diagnostics
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.name.FqName
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
import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass
import org.jetbrains.kotlin.resolve.konan.diagnostics.NativeObjCRefinementOverridesChecker.check
object NativeObjCRefinementChecker : DeclarationChecker {
val hidesFromObjCFqName = FqName("kotlin.native.HidesFromObjC")
val refinesInSwiftFqName = FqName("kotlin.native.RefinesInSwift")
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
if (descriptor !is CallableMemberDescriptor) return
if (descriptor !is FunctionDescriptor && descriptor !is PropertyDescriptor) return
val (objCAnnotations, swiftAnnotations) = descriptor.findRefinedAnnotations()
if (objCAnnotations.isNotEmpty() && swiftAnnotations.isNotEmpty()) {
swiftAnnotations.forEach {
val reportLocation = DescriptorToSourceUtils.getSourceFromAnnotation(it) ?: declaration
context.trace.report(ErrorsNative.REDUNDANT_SWIFT_REFINEMENT.on(reportLocation))
}
}
check(declaration, descriptor, context, objCAnnotations, swiftAnnotations)
}
private fun DeclarationDescriptor.findRefinedAnnotations(): Pair<List<AnnotationDescriptor>, List<AnnotationDescriptor>> {
val objCAnnotations = mutableListOf<AnnotationDescriptor>()
val swiftAnnotations = mutableListOf<AnnotationDescriptor>()
for (annotation in annotations) {
val annotations = annotation.annotationClass?.annotations ?: continue
for (metaAnnotation in annotations) {
when (metaAnnotation.fqName) {
hidesFromObjCFqName -> {
objCAnnotations.add(annotation)
break
}
refinesInSwiftFqName -> {
swiftAnnotations.add(annotation)
break
}
}
}
}
return objCAnnotations to swiftAnnotations
}
}
@@ -0,0 +1,108 @@
/*
* 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.resolve.konan.diagnostics
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker
import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext
import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass
import org.jetbrains.kotlin.resolve.konan.diagnostics.NativeObjCRefinementChecker.hidesFromObjCFqName
import org.jetbrains.kotlin.resolve.konan.diagnostics.NativeObjCRefinementChecker.refinesInSwiftFqName
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.resolve.scopes.MemberScope
object NativeObjCRefinementOverridesChecker : DeclarationChecker {
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
if (descriptor !is ClassDescriptor) return
descriptor.defaultType.memberScope
.getContributedDescriptors(DescriptorKindFilter.ALL, MemberScope.Companion.ALL_NAME_FILTER)
.forEach {
if (it !is CallableMemberDescriptor || it.kind.isReal) return@forEach
check(declaration, it, context, emptyList(), emptyList())
}
}
fun check(
declarationToReport: KtDeclaration,
descriptor: CallableMemberDescriptor,
context: DeclarationCheckerContext,
objCAnnotations: List<AnnotationDescriptor>,
swiftAnnotations: List<AnnotationDescriptor>
) {
if (descriptor.overriddenDescriptors.isEmpty()) return
var isHiddenFromObjC = objCAnnotations.isNotEmpty()
var isRefinedInSwift = swiftAnnotations.isNotEmpty()
val supersNotHiddenFromObjC = mutableListOf<CallableMemberDescriptor>()
val supersNotRefinedInSwift = mutableListOf<CallableMemberDescriptor>()
for (overriddenDescriptor in descriptor.overriddenDescriptors) {
val (superIsHiddenFromObjC, superIsRefinedInSwift) = overriddenDescriptor.inheritsRefinedAnnotations()
if (superIsHiddenFromObjC) isHiddenFromObjC = true else supersNotHiddenFromObjC.add(overriddenDescriptor)
if (superIsRefinedInSwift) isRefinedInSwift = true else supersNotRefinedInSwift.add(overriddenDescriptor)
}
if (isHiddenFromObjC && supersNotHiddenFromObjC.isNotEmpty()) {
context.trace.reportIncompatibleOverride(declarationToReport, descriptor, objCAnnotations, supersNotHiddenFromObjC)
}
if (isRefinedInSwift && supersNotRefinedInSwift.isNotEmpty()) {
context.trace.reportIncompatibleOverride(declarationToReport, descriptor, swiftAnnotations, supersNotRefinedInSwift)
}
}
private fun CallableMemberDescriptor.inheritsRefinedAnnotations(): Pair<Boolean, Boolean> {
val (hasObjC, hasSwift) = hasRefinedAnnotations()
if (hasObjC && hasSwift) return true to true
if (overriddenDescriptors.isEmpty()) return hasObjC to hasSwift
// Note: `checkOverrides` requires all overridden descriptors to be either refined or not refined.
val (inheritsObjC, inheritsSwift) = overriddenDescriptors.first().inheritsRefinedAnnotations()
return (hasObjC || inheritsObjC) to (hasSwift || inheritsSwift)
}
private fun CallableMemberDescriptor.hasRefinedAnnotations(): Pair<Boolean, Boolean> {
var hasObjC = false
var hasSwift = false
for (annotation in annotations) {
val annotations = annotation.annotationClass?.annotations ?: continue
for (metaAnnotation in annotations) {
when (metaAnnotation.fqName) {
hidesFromObjCFqName -> {
hasObjC = true
break
}
refinesInSwiftFqName -> {
hasSwift = true
break
}
}
}
if (hasObjC && hasSwift) return true to true
}
return hasObjC to hasSwift
}
private fun BindingTrace.reportIncompatibleOverride(
declaration: KtDeclaration,
descriptor: CallableMemberDescriptor,
annotations: List<AnnotationDescriptor>,
notRefinedSupers: List<CallableMemberDescriptor>
) {
val containingDeclarations = notRefinedSupers.map { it.containingDeclaration }
if (annotations.isEmpty()) {
report(ErrorsNative.INCOMPATIBLE_OBJC_REFINEMENT_OVERRIDE.on(declaration, descriptor, containingDeclarations))
} else {
annotations.forEach {
val reportLocation = DescriptorToSourceUtils.getSourceFromAnnotation(it) ?: declaration
report(ErrorsNative.INCOMPATIBLE_OBJC_REFINEMENT_OVERRIDE.on(reportLocation, descriptor, containingDeclarations))
}
}
}
}
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.resolve.konan.platform
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.container.StorageComponentContainer
import org.jetbrains.kotlin.container.useImpl
import org.jetbrains.kotlin.container.useInstance
@@ -13,7 +12,6 @@ import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.psi.KtCallableDeclaration
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.PlatformConfiguratorBase
import org.jetbrains.kotlin.resolve.calls.checkers.TypeOfChecker
import org.jetbrains.kotlin.resolve.checkers.ExpectedActualDeclarationChecker
import org.jetbrains.kotlin.resolve.inline.ReasonableInlineRule
import org.jetbrains.kotlin.resolve.jvm.checkers.SuperCallWithDefaultArgumentsChecker
@@ -26,7 +24,8 @@ object NativePlatformConfigurator : PlatformConfiguratorBase(
additionalDeclarationCheckers = listOf(
NativeThrowsChecker, NativeSharedImmutableChecker,
NativeTopLevelSingletonChecker, NativeThreadLocalChecker,
NativeObjCNameChecker
NativeObjCNameChecker, NativeObjCRefinementChecker,
NativeObjCRefinementAnnotationChecker, NativeObjCRefinementOverridesChecker
)
) {
override fun configureModuleComponents(container: StorageComponentContainer) {