From 203af3afddda5c840199022071597fe32ba1c5cf Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Fri, 19 Aug 2022 16:46:15 +0200 Subject: [PATCH] [FE 1.0] Add NativeObjCRefinement checkers --- .../diagnostics/nativeTests/objCRefinement.kt | 131 ++++++++++++++ .../nativeTests/objCRefinement.txt | 161 ++++++++++++++++++ .../DiagnosticsNativeTestGenerated.java | 6 + .../diagnostics/DefaultErrorMessagesNative.kt | 11 ++ .../resolve/konan/diagnostics/ErrorsNative.kt | 8 +- .../NativeObjCRefinementAnnotationChecker.kt | 62 +++++++ .../NativeObjCRefinementChecker.kt | 57 +++++++ .../NativeObjCRefinementOverridesChecker.kt | 108 ++++++++++++ .../platform/NativePlatformConfigurator.kt | 5 +- 9 files changed, 545 insertions(+), 4 deletions(-) create mode 100644 compiler/testData/diagnostics/nativeTests/objCRefinement.kt create mode 100644 compiler/testData/diagnostics/nativeTests/objCRefinement.txt create mode 100644 native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/NativeObjCRefinementAnnotationChecker.kt create mode 100644 native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/NativeObjCRefinementChecker.kt create mode 100644 native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/NativeObjCRefinementOverridesChecker.kt diff --git a/compiler/testData/diagnostics/nativeTests/objCRefinement.kt b/compiler/testData/diagnostics/nativeTests/objCRefinement.kt new file mode 100644 index 00000000000..2c4c0616647 --- /dev/null +++ b/compiler/testData/diagnostics/nativeTests/objCRefinement.kt @@ -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 +@RefinesInSwift +@Target(AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION) +@Retention(AnnotationRetention.BINARY) +annotation class MyRefinedAnnotationA + +@HidesFromObjC +@Target(AnnotationTarget.PROPERTY, AnnotationTarget.CLASS) +@Retention(AnnotationRetention.BINARY) +annotation class MyRefinedAnnotationB + +@RefinesInSwift +@Retention(AnnotationRetention.BINARY) +annotation class MyRefinedAnnotationC + +@RefinesInSwift +@Target(AnnotationTarget.PROPERTY) +@Retention(AnnotationRetention.BINARY) +annotation class MyRefinedAnnotationD + +@HiddenFromObjC +@ShouldRefineInSwift +var refinedProperty: Int = 0 + +@PluginHiddenFromObjC +@PluginShouldRefineInSwift +fun pluginRefinedFunction() { } + +@HiddenFromObjC +@PluginHiddenFromObjC +fun multipleObjCRefinementsFunction() { } + +@ShouldRefineInSwift +@PluginShouldRefineInSwift +fun multipleSwiftRefinementsFunction() { } + +@HiddenFromObjC +@PluginHiddenFromObjC +@ShouldRefineInSwift +@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 { + @HiddenFromObjC + override val barA: Int = 0 + @ShouldRefineInSwift + override val barB: Int = 0 + override fun fooA() { } + override fun fooB() { } + @HiddenFromObjC + open fun fooC() { } +} + +class ClassB: ClassA() { + @HiddenFromObjC + override fun fooB() { } + @ShouldRefineInSwift + override fun fooC() { } +} + +open class Base { + @HiddenFromObjC + open fun foo() {} +} + +interface I { + fun foo() +} + +open class Derived : Base(), I + +open class Derived2 : Derived() { + override fun foo() {} +} diff --git a/compiler/testData/diagnostics/nativeTests/objCRefinement.txt b/compiler/testData/diagnostics/nativeTests/objCRefinement.txt new file mode 100644 index 00000000000..3f0a362ca56 --- /dev/null +++ b/compiler/testData/diagnostics/nativeTests/objCRefinement.txt @@ -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 + } +} diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticsNativeTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticsNativeTestGenerated.java index c8620ac6279..6c40648b728 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticsNativeTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticsNativeTestGenerated.java @@ -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 { 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 7406ec4300e..1d438d7f3a8 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 @@ -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" + ) } } 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 c7374b1b3a5..595ad92e4f5 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 @@ -52,8 +52,14 @@ object ErrorsNative { val MISSING_EXACT_OBJC_NAME = DiagnosticFactory0.create(Severity.ERROR) @JvmField val NON_LITERAL_OBJC_NAME_ARG = DiagnosticFactory0.create(Severity.ERROR) + @JvmField + val REDUNDANT_SWIFT_REFINEMENT = DiagnosticFactory0.create(Severity.ERROR) + @JvmField + val INCOMPATIBLE_OBJC_REFINEMENT_OVERRIDE = DiagnosticFactory2.create>(Severity.ERROR) + @JvmField + val INVALID_OBJC_REFINEMENT_TARGETS = DiagnosticFactory0.create(Severity.ERROR) init { Errors.Initializer.initializeFactoryNames(ErrorsNative::class.java) } -} \ No newline at end of file +} diff --git a/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/NativeObjCRefinementAnnotationChecker.kt b/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/NativeObjCRefinementAnnotationChecker.kt new file mode 100644 index 00000000000..da139e224b3 --- /dev/null +++ b/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/NativeObjCRefinementAnnotationChecker.kt @@ -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 { + 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)) + } +} diff --git a/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/NativeObjCRefinementChecker.kt b/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/NativeObjCRefinementChecker.kt new file mode 100644 index 00000000000..caf84f9b403 --- /dev/null +++ b/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/NativeObjCRefinementChecker.kt @@ -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> { + val objCAnnotations = mutableListOf() + val swiftAnnotations = mutableListOf() + 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 + } +} diff --git a/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/NativeObjCRefinementOverridesChecker.kt b/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/NativeObjCRefinementOverridesChecker.kt new file mode 100644 index 00000000000..ff2cfcc9650 --- /dev/null +++ b/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/NativeObjCRefinementOverridesChecker.kt @@ -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, + swiftAnnotations: List + ) { + if (descriptor.overriddenDescriptors.isEmpty()) return + var isHiddenFromObjC = objCAnnotations.isNotEmpty() + var isRefinedInSwift = swiftAnnotations.isNotEmpty() + val supersNotHiddenFromObjC = mutableListOf() + val supersNotRefinedInSwift = mutableListOf() + 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 { + 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 { + 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, + notRefinedSupers: List + ) { + 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)) + } + } + } +} 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 823320aac99..5e32d30b5a7 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 @@ -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) {