Move optional annotation utilities to module 'resolution'

To avoid depending on a heavy module 'frontend' in parts of the compiler
where checking for optional annotations is needed, such as in
'ir.serialization.common'.
This commit is contained in:
Alexander Udalov
2021-04-22 20:31:53 +02:00
parent e790fa8ac9
commit bcf47ddc94
15 changed files with 67 additions and 70 deletions
@@ -38,6 +38,7 @@ import org.jetbrains.kotlin.resolve.multiplatform.ExpectedActualResolver.Compati
import org.jetbrains.kotlin.resolve.multiplatform.ExpectedActualResolver.Compatibility.Compatible
import org.jetbrains.kotlin.resolve.multiplatform.ExpectedActualResolver.Compatibility.Incompatible
import org.jetbrains.kotlin.resolve.multiplatform.ModuleFilter
import org.jetbrains.kotlin.resolve.multiplatform.OptionalAnnotationUtil
import org.jetbrains.kotlin.resolve.source.PsiSourceFile
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
@@ -171,7 +172,7 @@ class ExpectedActualDeclarationChecker(
val compatibility = ExpectedActualResolver.findActualForExpected(descriptor, module, moduleVisibilityFilter) ?: return
// Only strong incompatibilities, but this is an OptionalExpectation -- don't report it
if (compatibility.allStrongIncompatibilities() && isOptionalAnnotationClass(descriptor)) return
if (compatibility.allStrongIncompatibilities() && OptionalAnnotationUtil.isOptionalAnnotationClass(descriptor)) return
// Only strong incompatibilities, or error won't be reported on actual: report NO_ACTUAL_FOR_EXPECT here
if (compatibility.allStrongIncompatibilities() ||
@@ -389,41 +390,7 @@ class ExpectedActualDeclarationChecker(
}
companion object {
val OPTIONAL_EXPECTATION_FQ_NAME = FqName("kotlin.OptionalExpectation")
@JvmStatic
fun isOptionalAnnotationClass(descriptor: DeclarationDescriptor): Boolean =
descriptor is ClassDescriptor &&
descriptor.kind == ClassKind.ANNOTATION_CLASS &&
descriptor.isExpect &&
descriptor.annotations.hasAnnotation(OPTIONAL_EXPECTATION_FQ_NAME)
// TODO: move to some other place which is accessible both from backend-common and js.serializer
@JvmStatic
fun shouldGenerateExpectClass(descriptor: ClassDescriptor): Boolean {
assert(descriptor.isExpect) { "Not an expected class: $descriptor" }
if (isOptionalAnnotationClass(descriptor)) {
with(ExpectedActualResolver) {
return descriptor.findCompatibleActualForExpected(descriptor.module).isEmpty()
}
}
return false
}
fun Map<out Compatibility, Collection<MemberDescriptor>>.allStrongIncompatibilities(): Boolean =
this.keys.all { it is Incompatible && it.kind == Compatibility.IncompatibilityKind.STRONG }
private fun <K, V> LinkedHashMap<K, List<V>>.merge(other: Map<K, List<V>>): LinkedHashMap<K, List<V>> {
for ((key, newValue) in other) {
val oldValue = this[key] ?: emptyList()
this[key] = oldValue + newValue
}
return this
}
private fun ModuleInfo.unwrapModuleInfo(): List<ModuleInfo> = if (this is CombinedModuleInfo) this.containedModules else listOf(this)
}
}
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.multiplatform.OptionalAnnotationUtil
object OptionalExpectationChecker {
fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, trace: BindingTrace) {
@@ -34,6 +35,6 @@ object OptionalExpectationChecker {
private fun getOptionalExpectationEntry(declaration: KtDeclaration, trace: BindingTrace): KtAnnotationEntry? =
declaration.annotationEntries.find { entry ->
val annotationDescriptor = trace.get(BindingContext.ANNOTATION, entry)
annotationDescriptor?.fqName == ExpectedActualDeclarationChecker.OPTIONAL_EXPECTATION_FQ_NAME
annotationDescriptor?.fqName == OptionalAnnotationUtil.OPTIONAL_EXPECTATION_FQ_NAME
}
}
@@ -8,14 +8,15 @@ package org.jetbrains.kotlin.resolve.checkers
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.platform.isCommon
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.descriptorUtil.platform
import org.jetbrains.kotlin.platform.isCommon
import org.jetbrains.kotlin.resolve.multiplatform.OptionalAnnotationUtil
import org.jetbrains.kotlin.resolve.multiplatform.isCommonSource
class OptionalExpectationUsageChecker : ClassifierUsageChecker {
override fun check(targetDescriptor: ClassifierDescriptor, element: PsiElement, context: ClassifierUsageCheckerContext) {
if (!ExpectedActualDeclarationChecker.isOptionalAnnotationClass(targetDescriptor)) return
if (!OptionalAnnotationUtil.isOptionalAnnotationClass(targetDescriptor)) return
if (!element.isUsageAsAnnotationOrImport()) {
context.trace.report(Errors.OPTIONAL_DECLARATION_OUTSIDE_OF_ANNOTATION_ENTRY.on(element))