[K/N] KT-58562: Implement frontend checkers for HiddenFromObjC on classes
This commit is contained in:
committed by
Space Team
parent
f3a22e0ac4
commit
c57c34525f
+10
-2
@@ -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)
|
||||
|
||||
+61
@@ -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)
|
||||
}
|
||||
+32
-27
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ object NativePlatformConfigurator : PlatformConfiguratorBase(
|
||||
NativeTopLevelSingletonChecker, NativeThreadLocalChecker,
|
||||
NativeObjCNameChecker, NativeObjCNameOverridesChecker,
|
||||
NativeObjCRefinementChecker, NativeObjCRefinementAnnotationChecker,
|
||||
NativeObjCRefinementOverridesChecker
|
||||
NativeObjCRefinementOverridesChecker, NativeHiddenFromObjCInheritanceChecker
|
||||
)
|
||||
) {
|
||||
override fun configureModuleComponents(container: StorageComponentContainer) {
|
||||
|
||||
Reference in New Issue
Block a user