[FE 1.0] Add NativeObjCRefinement checkers
This commit is contained in:
committed by
SvyatoslavScherbina
parent
6ae517a2df
commit
203af3afdd
+11
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+62
@@ -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))
|
||||
}
|
||||
}
|
||||
+57
@@ -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
|
||||
}
|
||||
}
|
||||
+108
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-3
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user