[K/N] KT-58562: Implement frontend checkers for HiddenFromObjC on classes

This commit is contained in:
Sergey Bogolepov
2023-05-19 11:33:17 +00:00
committed by Space Team
parent f3a22e0ac4
commit c57c34525f
14 changed files with 465 additions and 85 deletions
@@ -46,7 +46,6 @@ object NATIVE_DIAGNOSTICS_LIST : DiagnosticList("FirNativeErrors") {
parameter<FirBasedSymbol<*>>("declaration")
parameter<Collection<FirRegularClassSymbol>>("containingClasses")
}
val INVALID_OBJC_REFINEMENT_TARGETS by error<KtElement>()
val INAPPLICABLE_OBJC_NAME by error<KtElement>()
val INVALID_OBJC_NAME by error<KtElement>()
val INVALID_OBJC_NAME_CHARS by error<KtElement> {
@@ -63,5 +62,8 @@ object NATIVE_DIAGNOSTICS_LIST : DiagnosticList("FirNativeErrors") {
val INAPPLICABLE_EXACT_OBJC_NAME by error<KtElement>()
val MISSING_EXACT_OBJC_NAME by error<KtElement>()
val NON_LITERAL_OBJC_NAME_ARG by error<KtElement>()
val INVALID_OBJC_HIDES_TARGETS by error<KtElement>()
val INVALID_REFINES_IN_SWIFT_TARGETS by error<KtElement>()
val SUBTYPE_OF_HIDDEN_FROM_OBJC by error<KtElement>()
}
}
@@ -35,7 +35,6 @@ object FirNativeErrors {
val INVALID_CHARACTERS_NATIVE by deprecationError1<PsiElement, String>(ProhibitInvalidCharsInNativeIdentifiers, SourceElementPositioningStrategies.NAME_IDENTIFIER)
val REDUNDANT_SWIFT_REFINEMENT by error0<KtElement>()
val INCOMPATIBLE_OBJC_REFINEMENT_OVERRIDE by error2<KtElement, FirBasedSymbol<*>, Collection<FirRegularClassSymbol>>()
val INVALID_OBJC_REFINEMENT_TARGETS by error0<KtElement>()
val INAPPLICABLE_OBJC_NAME by error0<KtElement>()
val INVALID_OBJC_NAME by error0<KtElement>()
val INVALID_OBJC_NAME_CHARS by error1<KtElement, String>()
@@ -45,6 +44,9 @@ object FirNativeErrors {
val INAPPLICABLE_EXACT_OBJC_NAME by error0<KtElement>()
val MISSING_EXACT_OBJC_NAME by error0<KtElement>()
val NON_LITERAL_OBJC_NAME_ARG by error0<KtElement>()
val INVALID_OBJC_HIDES_TARGETS by error0<KtElement>()
val INVALID_REFINES_IN_SWIFT_TARGETS by error0<KtElement>()
val SUBTYPE_OF_HIDDEN_FROM_OBJC by error0<KtElement>()
init {
RootDiagnosticRendererFactory.registerFactory(FirNativeErrorsDefaultMessages)
@@ -22,14 +22,16 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.INCO
import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.INCOMPATIBLE_THROWS_INHERITED
import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.INCOMPATIBLE_THROWS_OVERRIDE
import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.INVALID_CHARACTERS_NATIVE
import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.INVALID_OBJC_HIDES_TARGETS
import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.INVALID_OBJC_NAME
import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.INVALID_OBJC_NAME_CHARS
import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.INVALID_OBJC_NAME_FIRST_CHAR
import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.INVALID_OBJC_REFINEMENT_TARGETS
import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.INVALID_REFINES_IN_SWIFT_TARGETS
import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.MISSING_EXACT_OBJC_NAME
import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.MISSING_EXCEPTION_IN_THROWS_ON_SUSPEND
import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.REDUNDANT_SWIFT_REFINEMENT
import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.NON_LITERAL_OBJC_NAME_ARG
import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.SUBTYPE_OF_HIDDEN_FROM_OBJC
import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.THROWS_LIST_EMPTY
object FirNativeErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
@@ -60,8 +62,12 @@ object FirNativeErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
SYMBOLS
)
map.put(
INVALID_OBJC_REFINEMENT_TARGETS,
"Refines annotations are only applicable to annotations with targets CLASS, FUNCTION and/or PROPERTY"
INVALID_OBJC_HIDES_TARGETS,
"@HidesFromObjC annotation is only applicable to annotations with targets CLASS, FUNCTION and/or PROPERTY"
)
map.put(
INVALID_REFINES_IN_SWIFT_TARGETS,
"@RefinesInSwift annotation is only applicable to annotations with targets FUNCTION and/or PROPERTY"
)
map.put(INAPPLICABLE_OBJC_NAME, "@ObjCName is not applicable on overrides")
map.put(INVALID_OBJC_NAME, "@ObjCName should have a name and/or swiftName")
@@ -72,5 +78,6 @@ object FirNativeErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
map.put(INAPPLICABLE_EXACT_OBJC_NAME, "Exact @ObjCName is only applicable to classes, objects and interfaces")
map.put(MISSING_EXACT_OBJC_NAME, "Exact @ObjCName is required to have an ObjC name")
map.put(NON_LITERAL_OBJC_NAME_ARG, "@ObjCName accepts only literal string and boolean values")
map.put(SUBTYPE_OF_HIDDEN_FROM_OBJC, "Only @HiddenFromObjC declaration can be a subtype of @HiddenFromObjC declaration")
}
}
@@ -0,0 +1,72 @@
/*
* Copyright 2010-2023 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.descriptors.ClassKind
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.checkers.classKind
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirRegularClassChecker
import org.jetbrains.kotlin.fir.analysis.checkers.getContainingClassSymbol
import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.toAnnotationClassLikeSymbol
import org.jetbrains.kotlin.fir.declarations.utils.superConeTypes
import org.jetbrains.kotlin.fir.declarations.utils.visibility
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.types.*
/**
* Check that the given class does not inherit from class or implements interface that is
* marked as HiddenFromObjC (aka "marked with annotation that is marked as HidesFromObjC").
*/
object FirNativeHiddenFromObjCInheritanceChecker : FirRegularClassChecker() {
override fun check(declaration: FirRegularClass, context: CheckerContext, reporter: DiagnosticReporter) {
// Enum entries inherit from their enum class.
if (declaration.classKind == ClassKind.ENUM_ENTRY) {
return
}
// Non-public types do not leak to Objective-C API surface, so it is OK for them
// to inherit from hidden types.
if (!declaration.visibility.isPublicAPI) return
val session = context.session
// No need to report anything on class that is hidden itself.
if (checkIsHiddenFromObjC(declaration.symbol, session)) {
return
}
val superTypes = declaration.superConeTypes
.filterNot { it.isAny || it.isNullableAny }
.mapNotNull { it.toSymbol(session) }
superTypes.firstOrNull { st -> checkIsHiddenFromObjC(st, session) }?.let {
reporter.reportOn(declaration.source, FirNativeErrors.SUBTYPE_OF_HIDDEN_FROM_OBJC, context)
}
}
}
private fun checkContainingClassIsHidden(classSymbol: FirClassLikeSymbol<*>, session: FirSession): Boolean {
return classSymbol.getContainingClassSymbol(session)?.let {
if (checkIsHiddenFromObjC(it, session)) {
true
} else {
checkContainingClassIsHidden(it, session)
}
} ?: false
}
private fun checkIsHiddenFromObjC(classSymbol: FirClassLikeSymbol<*>, session: FirSession): Boolean {
classSymbol.annotations.forEach { annotation ->
val annotationClass = annotation.toAnnotationClassLikeSymbol(session) ?: return@forEach
val objCExportMetaAnnotations = annotationClass.annotations.findMetaAnnotations(session)
if (objCExportMetaAnnotations.hidesFromObjCAnnotation != null) {
return true
}
}
return checkContainingClassIsHidden(classSymbol, session)
}
@@ -6,9 +6,11 @@
package org.jetbrains.kotlin.fir.analysis.native.checkers
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.FirAnnotationContainer
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirRegularClassChecker
@@ -21,12 +23,13 @@ import org.jetbrains.kotlin.fir.expressions.FirAnnotation
object FirNativeObjCRefinementAnnotationChecker : FirRegularClassChecker() {
private val supportedTargets = arrayOf(KotlinTarget.FUNCTION, KotlinTarget.PROPERTY, KotlinTarget.CLASS)
private val hidesFromObjCSupportedTargets = arrayOf(KotlinTarget.FUNCTION, KotlinTarget.PROPERTY, KotlinTarget.CLASS)
private val refinesInSwiftSupportedTargets = arrayOf(KotlinTarget.FUNCTION, KotlinTarget.PROPERTY)
override fun check(declaration: FirRegularClass, context: CheckerContext, reporter: DiagnosticReporter) {
if (declaration.classKind != ClassKind.ANNOTATION_CLASS) return
val session = context.session
val (objCAnnotation, swiftAnnotation) = declaration.findMetaAnnotations(session)
val (objCAnnotation, swiftAnnotation) = declaration.annotations.findMetaAnnotations(session)
if (objCAnnotation == null && swiftAnnotation == null) return
if (objCAnnotation != null && swiftAnnotation != null) {
reporter.reportOn(
@@ -36,23 +39,35 @@ object FirNativeObjCRefinementAnnotationChecker : FirRegularClassChecker() {
)
}
val targets = declaration.getAllowedAnnotationTargets(session)
val unsupportedTargets = targets - supportedTargets
if (unsupportedTargets.isNotEmpty()) {
objCAnnotation?.let { reporter.reportOn(it.source, FirNativeErrors.INVALID_OBJC_REFINEMENT_TARGETS, context) }
swiftAnnotation?.let { reporter.reportOn(it.source, FirNativeErrors.INVALID_OBJC_REFINEMENT_TARGETS, context) }
}
}
private fun FirRegularClass.findMetaAnnotations(session: FirSession): Pair<FirAnnotation?, FirAnnotation?> {
var objCAnnotation: FirAnnotation? = null
var swiftAnnotation: FirAnnotation? = null
for (annotation in annotations) {
when (annotation.toAnnotationClassId(session)) {
hidesFromObjCClassId -> objCAnnotation = annotation
refinesInSwiftClassId -> swiftAnnotation = annotation
objCAnnotation?.let {
if ((targets - hidesFromObjCSupportedTargets).isNotEmpty()) {
reporter.reportOn(it.source, FirNativeErrors.INVALID_OBJC_HIDES_TARGETS, context)
}
}
swiftAnnotation?.let {
if ((targets - refinesInSwiftSupportedTargets).isNotEmpty()) {
reporter.reportOn(it.source, FirNativeErrors.INVALID_REFINES_IN_SWIFT_TARGETS, context)
}
if (objCAnnotation != null && swiftAnnotation != null) break
}
return objCAnnotation to swiftAnnotation
}
}
internal data class ObjCExportMetaAnnotations(
val hidesFromObjCAnnotation: FirAnnotation?,
val refinesInSwiftAnnotation: FirAnnotation?,
)
internal fun List<FirAnnotation>.findMetaAnnotations(session: FirSession): ObjCExportMetaAnnotations {
var objCAnnotation: FirAnnotation? = null
var swiftAnnotation: FirAnnotation? = null
for (annotation in this) {
when (annotation.toAnnotationClassId(session)) {
hidesFromObjCClassId -> objCAnnotation = annotation
refinesInSwiftClassId -> swiftAnnotation = annotation
}
if (objCAnnotation != null && swiftAnnotation != null) break
}
return ObjCExportMetaAnnotations(objCAnnotation, swiftAnnotation)
}
@@ -30,6 +30,7 @@ object NativeDeclarationCheckers : DeclarationCheckers() {
override val regularClassCheckers: Set<FirRegularClassChecker>
get() = setOf(
FirNativeObjCRefinementAnnotationChecker
FirNativeObjCRefinementAnnotationChecker,
FirNativeHiddenFromObjCInheritanceChecker,
)
}
+71 -3
View File
@@ -8,7 +8,7 @@ package kotlin.native
annotation class HidesFromObjC
@HidesFromObjC
@Target(AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION)
@Target(AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
@Retention(AnnotationRetention.BINARY)
annotation class HiddenFromObjC
@@ -21,6 +21,11 @@ annotation class RefinesInSwift
@Retention(AnnotationRetention.BINARY)
public annotation class ShouldRefineInSwift
<!INVALID_REFINES_IN_SWIFT_TARGETS!>@RefinesInSwift<!>
@Target(AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
@Retention(AnnotationRetention.BINARY)
public annotation class WrongShouldRefineInSwift
// FILE: plugin.kt
package plugin
@@ -44,12 +49,12 @@ import plugin.PluginShouldRefineInSwift
@Retention(AnnotationRetention.BINARY)
annotation class MyRefinedAnnotationA
<!INVALID_OBJC_REFINEMENT_TARGETS!>@HidesFromObjC<!>
<!INVALID_OBJC_HIDES_TARGETS!>@HidesFromObjC<!>
@Target(AnnotationTarget.PROPERTY, AnnotationTarget.FILE)
@Retention(AnnotationRetention.BINARY)
annotation class MyRefinedAnnotationB
<!INVALID_OBJC_REFINEMENT_TARGETS!>@RefinesInSwift<!>
<!INVALID_REFINES_IN_SWIFT_TARGETS!>@RefinesInSwift<!>
@Retention(AnnotationRetention.BINARY)
annotation class MyRefinedAnnotationC
@@ -132,3 +137,66 @@ interface I {
open class Derived2 : Derived() {
override fun foo() {}
}
@HiddenFromObjC
open class OpenHiddenClass
<!SUBTYPE_OF_HIDDEN_FROM_OBJC!>class InheritsFromOpenHiddenClass : OpenHiddenClass()<!>
@HiddenFromObjC
interface HiddenInterface
interface NotHiddenInterface
<!SUBTYPE_OF_HIDDEN_FROM_OBJC!>class ImplementsHiddenInterface : NotHiddenInterface, HiddenInterface<!>
<!SUBTYPE_OF_HIDDEN_FROM_OBJC!>class InheritsFromOpenHiddenClass2 : NotHiddenInterface, OpenHiddenClass()<!>
@HiddenFromObjC
class OuterHidden {
class Nested {
open class Nested
}
}
<!SUBTYPE_OF_HIDDEN_FROM_OBJC!>class InheritsFromNested : OuterHidden.Nested.Nested()<!>
private class PrivateInheritsFromNested : OuterHidden.Nested.Nested()
internal class InternalInheritsFromNested : OuterHidden.Nested.Nested()
fun produceInstanceOfHidden(): OuterHidden.Nested.Nested {
return object : OuterHidden.Nested.Nested() {}
}
@HiddenFromObjC
enum class MyHiddenEnum {
A,
B,
C
}
@HiddenFromObjC
object MyHiddenObject
sealed class MySealedClass {
@HiddenFromObjC
class MyHiddenSealedVariant : MySealedClass()
class MyPublicVariant : MySealedClass()
}
@HiddenFromObjC
enum class MyHiddenNonTrivialEnum {
A,
B,
C {
override fun sayCheese(): String {
return "Boo :("
}
};
open fun sayCheese(): String {
return "Cheese!"
}
}
+163 -1
View File
@@ -5,6 +5,7 @@ package
@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 fun produceInstanceOfHidden(): OuterHidden.Nested.Nested
public open class Base {
public constructor Base()
@@ -54,6 +55,12 @@ public open class Derived2 : Derived {
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@kotlin.native.HiddenFromObjC public interface HiddenInterface {
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
}
public interface I {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public abstract fun foo(): kotlin.Unit
@@ -61,6 +68,34 @@ public interface I {
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final class ImplementsHiddenInterface : NotHiddenInterface, HiddenInterface {
public constructor ImplementsHiddenInterface()
public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String
}
public final class InheritsFromNested : OuterHidden.Nested.Nested {
public constructor InheritsFromNested()
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
}
public final class InheritsFromOpenHiddenClass : OpenHiddenClass {
public constructor InheritsFromOpenHiddenClass()
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
}
public final class InheritsFromOpenHiddenClass2 : NotHiddenInterface, OpenHiddenClass {
public constructor InheritsFromOpenHiddenClass2()
public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface InterfaceA {
public abstract val barA: kotlin.Int
public abstract val barB: kotlin.Int
@@ -81,6 +116,63 @@ public interface InterfaceB {
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
internal final class InternalInheritsFromNested : OuterHidden.Nested.Nested {
public constructor InternalInheritsFromNested()
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.HiddenFromObjC public final enum class MyHiddenEnum : kotlin.Enum<MyHiddenEnum> {
enum entry A
enum entry B
enum entry C
private constructor MyHiddenEnum()
@kotlin.internal.IntrinsicConstEvaluation public final override /*1*/ /*fake_override*/ val name: kotlin.String
public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int
protected final override /*1*/ /*fake_override*/ fun clone(): kotlin.Any
public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: MyHiddenEnum): kotlin.Int
public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
// Static members
public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): MyHiddenEnum
public final /*synthesized*/ fun values(): kotlin.Array<MyHiddenEnum>
}
@kotlin.native.HiddenFromObjC public final enum class MyHiddenNonTrivialEnum : kotlin.Enum<MyHiddenNonTrivialEnum> {
enum entry A
enum entry B
enum entry C
private constructor MyHiddenNonTrivialEnum()
@kotlin.internal.IntrinsicConstEvaluation public final override /*1*/ /*fake_override*/ val name: kotlin.String
public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int
protected final override /*1*/ /*fake_override*/ fun clone(): kotlin.Any
public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: MyHiddenNonTrivialEnum): kotlin.Int
public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open fun sayCheese(): kotlin.String
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
// Static members
public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): MyHiddenNonTrivialEnum
public final /*synthesized*/ fun values(): kotlin.Array<MyHiddenNonTrivialEnum>
}
@kotlin.native.HiddenFromObjC public object MyHiddenObject {
private constructor MyHiddenObject()
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.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
@@ -108,13 +200,75 @@ public interface InterfaceB {
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public sealed class MySealedClass {
protected constructor MySealedClass()
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.HiddenFromObjC public final class MyHiddenSealedVariant : MySealedClass {
public constructor MyHiddenSealedVariant()
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
}
public final class MyPublicVariant : MySealedClass {
public constructor MyPublicVariant()
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
}
}
public interface NotHiddenInterface {
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.HiddenFromObjC public open class OpenHiddenClass {
public constructor OpenHiddenClass()
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.HiddenFromObjC public final class OuterHidden {
public constructor OuterHidden()
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
public final class Nested {
public constructor Nested()
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
public open class Nested {
public constructor Nested()
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
}
}
}
private final class PrivateInheritsFromNested : OuterHidden.Nested.Nested {
public constructor PrivateInheritsFromNested()
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
}
public typealias HFOC = kotlin.native.HiddenFromObjC
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 {
@kotlin.native.HidesFromObjC @kotlin.annotation.Target(allowedTargets = {AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION, AnnotationTarget.CLASS}) @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
@@ -141,6 +295,13 @@ package kotlin {
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, AnnotationTarget.CLASS}) @kotlin.annotation.Retention(value = AnnotationRetention.BINARY) public final annotation class WrongShouldRefineInSwift : kotlin.Annotation {
public constructor WrongShouldRefineInSwift()
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
}
}
}
@@ -160,3 +321,4 @@ package plugin {
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
}
@@ -596,36 +596,9 @@ private class BackendChecker(
if (declaration.isKotlinObjCClass()) {
checkKotlinObjCClass(declaration)
}
checkHiddenFromObjCRestrictions(declaration)
super.visitClass(declaration)
}
private fun IrClass.isHiddenFromObjC(): Boolean {
return annotations.any { annotationCall ->
val annotationClass = annotationCall.symbol.owner.parentAsClass
annotationClass.annotations.hasAnnotation(KonanFqNames.hidesFromObjC)
}
}
private fun checkHiddenFromObjCRestrictions(declaration: IrClass) {
// If declaration itself. is marked as hidden, we don't need to do anything
if (declaration.isHiddenFromObjC()) return
// Enum entries inherit from their classes, so if enum class is marked as hidden, there is no need to
// produce additional errors.
if (declaration.kind == ClassKind.ENUM_ENTRY) return
// Private and local classes does not leak to Objective-C API surface, so it is OK for them
// to inherit from hidden types.
if (DescriptorVisibilities.isPrivate(declaration.visibility) || declaration.isLocal) return
if (declaration.superClass?.isHiddenFromObjC() == true) {
reportError(declaration, "Only @HiddenFromObjC declaration can inherit from class annotated with @HiddenFromObjC")
}
declaration.getSuperInterfaces().forEach {
if (it.isHiddenFromObjC()) {
reportError(declaration, "Only @HiddenFromObjC declaration can implement interface annotated with @HiddenFromObjC")
}
}
}
}
private fun BackendChecker.checkCanGenerateCCall(expression: IrCall, isInvoke: Boolean) {
@@ -63,8 +63,16 @@ private val DIAGNOSTIC_FACTORY_TO_RENDERER by lazy {
CommonRenderers.commaSeparated(Renderers.NAME)
)
put(
ErrorsNative.INVALID_OBJC_REFINEMENT_TARGETS,
"Refines annotations are only applicable to annotations with targets CLASS, FUNCTION and/or PROPERTY"
ErrorsNative.INVALID_OBJC_HIDES_TARGETS,
"@HidesFromObjC annotation is only applicable to annotations with targets CLASS, FUNCTION and/or PROPERTY"
)
put(
ErrorsNative.INVALID_REFINES_IN_SWIFT_TARGETS,
"@RefinesInSwift annotation is only applicable to annotations with targets FUNCTION and/or PROPERTY"
)
put(
ErrorsNative.SUBTYPE_OF_HIDDEN_FROM_OBJC,
"Only @HiddenFromObjC declaration can be a subtype of @HiddenFromObjC declaration"
)
}
}
@@ -59,7 +59,11 @@ object ErrorsNative {
@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)
val INVALID_OBJC_HIDES_TARGETS = DiagnosticFactory0.create<KtElement>(Severity.ERROR)
@JvmField
val INVALID_REFINES_IN_SWIFT_TARGETS = DiagnosticFactory0.create<KtElement>(Severity.ERROR)
@JvmField
val SUBTYPE_OF_HIDDEN_FROM_OBJC = DiagnosticFactory0.create<KtElement>(Severity.ERROR)
init {
Errors.Initializer.initializeFactoryNames(ErrorsNative::class.java)
@@ -0,0 +1,61 @@
/*
* Copyright 2010-2023 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.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtSuperTypeCallEntry
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.descriptorUtil.getSuperClassNotAny
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperInterfaces
/**
* Check that the given class does not inherit from class or implements interface that is
* marked as HiddenFromObjC (aka "marked with annotation that is marked as HidesFromObjC").
*/
object NativeHiddenFromObjCInheritanceChecker : DeclarationChecker {
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
if (descriptor !is ClassDescriptor) return
// Enum entries inherit from their enum class.
if (descriptor.kind == ClassKind.ENUM_ENTRY) return
// Non-public types do not leak to Objective-C API surface, so it is OK for them
// to inherit from hidden types.
if (!descriptor.visibility.isPublicAPI) return
// No need to report anything on class that is hidden itself.
if (checkClassIsHiddenFromObjC(descriptor)) return
val isSubtypeOfHiddenFromObjC = descriptor.getSuperInterfaces().any { checkClassIsHiddenFromObjC(it) } ||
descriptor.getSuperClassNotAny()?.let { checkClassIsHiddenFromObjC(it) } == true
if (isSubtypeOfHiddenFromObjC) {
context.trace.report(ErrorsNative.SUBTYPE_OF_HIDDEN_FROM_OBJC.on(declaration))
}
}
}
private fun checkContainingClassIsHidden(currentClass: ClassDescriptor): Boolean {
return (currentClass.containingDeclaration as? ClassDescriptor)?.let {
if (checkClassIsHiddenFromObjC(it)) {
true
} else {
checkContainingClassIsHidden(it)
}
} ?: false
}
private fun checkClassIsHiddenFromObjC(clazz: ClassDescriptor): Boolean {
clazz.annotations.forEach { annotation ->
val objcExportMetaAnnotations = annotation.annotationClass?.findObjCExportMetaAnnotations()
?: return@forEach
if (objcExportMetaAnnotations.hidesFromObjCAnnotation != null) {
return true
}
}
// If outer class is hidden then inner/nested class is hidden as well.
return checkContainingClassIsHidden(clazz)
}
@@ -12,51 +12,56 @@ 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
internal data class ObjCExportMetaAnnotations(
val hidesFromObjCAnnotation: AnnotationDescriptor?,
val refinesInSwiftAnnotation: AnnotationDescriptor?,
)
internal fun DeclarationDescriptor.findObjCExportMetaAnnotations(): ObjCExportMetaAnnotations {
require(this is ClassDescriptor && this.kind == ClassKind.ANNOTATION_CLASS)
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 ObjCExportMetaAnnotations(objCAnnotation, swiftAnnotation)
}
object NativeObjCRefinementAnnotationChecker : DeclarationChecker {
private val supportedTargets = arrayOf(KotlinTarget.FUNCTION, KotlinTarget.PROPERTY, KotlinTarget.CLASS)
private val hidesFromObjCSupportedTargets = arrayOf(KotlinTarget.FUNCTION, KotlinTarget.PROPERTY, KotlinTarget.CLASS)
private val refinesInSwiftSupportedTargets = 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()
val (objCAnnotation, swiftAnnotation) = descriptor.findObjCExportMetaAnnotations()
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
objCAnnotation?.let {
if ((targets - hidesFromObjCSupportedTargets).isNotEmpty()) {
val reportLocation = DescriptorToSourceUtils.getSourceFromAnnotation(it) ?: declaration
context.trace.report(ErrorsNative.INVALID_OBJC_HIDES_TARGETS.on(reportLocation))
}
}
swiftAnnotation?.let {
if ((targets - refinesInSwiftSupportedTargets).isNotEmpty()) {
val reportLocation = DescriptorToSourceUtils.getSourceFromAnnotation(it) ?: declaration
context.trace.report(ErrorsNative.INVALID_REFINES_IN_SWIFT_TARGETS.on(reportLocation))
}
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))
}
}
@@ -28,7 +28,7 @@ object NativePlatformConfigurator : PlatformConfiguratorBase(
NativeTopLevelSingletonChecker, NativeThreadLocalChecker,
NativeObjCNameChecker, NativeObjCNameOverridesChecker,
NativeObjCRefinementChecker, NativeObjCRefinementAnnotationChecker,
NativeObjCRefinementOverridesChecker
NativeObjCRefinementOverridesChecker, NativeHiddenFromObjCInheritanceChecker
)
) {
override fun configureModuleComponents(container: StorageComponentContainer) {