[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
@@ -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) {