From 7f1424737e833facb642c376b46d33917c13d4f5 Mon Sep 17 00:00:00 2001 From: Jinseong Jeon Date: Thu, 15 Jun 2023 19:23:22 -0700 Subject: [PATCH] LC: rework modality of enum class It is abstract if it has abstract member. It is final if it doesn't have enum entries that need subclass. Otherwise, it is open (i.e., no modifier) ^KT-57567 Fixed --- .../SymbolLightClassForNamedClassLike.kt | 5 ++++- .../modifierLists/GranularModifiersBox.kt | 9 +++++++- .../light/classes/symbol/symbolLightUtils.kt | 22 ++++++++++++++++--- .../kotlin/asJava/classes/KtLightClassImpl.kt | 22 +++++++++++++++++-- .../lightClassByPsi/enums.fir.java | 2 +- .../lightClasses/lightClassByPsi/enums.java | 4 ++-- .../asJava/KotlinLightClassStructureTest.java | 2 +- 7 files changed, 55 insertions(+), 11 deletions(-) diff --git a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/classes/SymbolLightClassForNamedClassLike.kt b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/classes/SymbolLightClassForNamedClassLike.kt index 12131726b2a..80d0bde4da9 100644 --- a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/classes/SymbolLightClassForNamedClassLike.kt +++ b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/classes/SymbolLightClassForNamedClassLike.kt @@ -142,7 +142,10 @@ abstract class SymbolLightClassForNamedClassLike : SymbolLightClassForClassLike< GranularModifiersBox.computeVisibilityForClass(ktModule, classOrObjectSymbolPointer, isTopLevel) } - in GranularModifiersBox.MODALITY_MODIFIERS -> GranularModifiersBox.computeSimpleModality(ktModule, classOrObjectSymbolPointer) + in GranularModifiersBox.MODALITY_MODIFIERS -> { + GranularModifiersBox.computeSimpleModality(ktModule, classOrObjectSymbolPointer) + } + PsiModifier.STATIC -> { val isStatic = !isTopLevel && !isInner mapOf(modifier to isStatic) diff --git a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/modifierLists/GranularModifiersBox.kt b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/modifierLists/GranularModifiersBox.kt index 28a8f2a7e8c..8c4b235528b 100644 --- a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/modifierLists/GranularModifiersBox.kt +++ b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/modifierLists/GranularModifiersBox.kt @@ -8,10 +8,13 @@ package org.jetbrains.kotlin.light.classes.symbol.modifierLists import com.intellij.psi.PsiModifier import kotlinx.collections.immutable.PersistentMap import kotlinx.collections.immutable.toPersistentHashMap +import org.jetbrains.kotlin.analysis.api.symbols.KtClassKind +import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithModality import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithVisibility import org.jetbrains.kotlin.analysis.api.symbols.pointers.KtSymbolPointer import org.jetbrains.kotlin.analysis.project.structure.KtModule +import org.jetbrains.kotlin.light.classes.symbol.* import org.jetbrains.kotlin.light.classes.symbol.computeSimpleModality import org.jetbrains.kotlin.light.classes.symbol.toPsiVisibilityForClass import org.jetbrains.kotlin.light.classes.symbol.toPsiVisibilityForMember @@ -89,7 +92,11 @@ internal class GranularModifiersBox( declarationPointer: KtSymbolPointer, ): PersistentMap { val modality = declarationPointer.withSymbol(ktModule) { - it.computeSimpleModality() + if ((it as? KtClassOrObjectSymbol)?.classKind == KtClassKind.ENUM_CLASS) { + it.enumClassModality() + } else { + it.computeSimpleModality() + } } return MODALITY_MODIFIERS_MAP.with(modality) diff --git a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/symbolLightUtils.kt b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/symbolLightUtils.kt index 6b5537f63f0..16bbaa3f39d 100644 --- a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/symbolLightUtils.kt +++ b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/symbolLightUtils.kt @@ -15,9 +15,7 @@ import org.jetbrains.kotlin.analysis.api.KtAnalysisSession import org.jetbrains.kotlin.analysis.api.annotations.* import org.jetbrains.kotlin.analysis.api.base.KtConstantValue import org.jetbrains.kotlin.analysis.api.components.DefaultTypeClassIds -import org.jetbrains.kotlin.analysis.api.symbols.KtKotlinPropertySymbol -import org.jetbrains.kotlin.analysis.api.symbols.KtPropertySymbol -import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol +import org.jetbrains.kotlin.analysis.api.symbols.* import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithModality import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithTypeParameters import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithVisibility @@ -78,6 +76,24 @@ internal fun KtSymbolWithModality.computeSimpleModality(): String? = when (modal Modality.OPEN -> null } +context(KtAnalysisSession) +internal fun KtClassOrObjectSymbol.enumClassModality(): String? { + if (getMemberScope().getCallableSymbols().any { (it as? KtSymbolWithModality)?.modality == Modality.ABSTRACT }) { + return PsiModifier.ABSTRACT + } + + if (getDeclaredMemberScope().getCallableSymbols().none { it is KtEnumEntrySymbol && it.requiresSubClass() }) { + return PsiModifier.FINAL + } + + return null +} + +context(KtAnalysisSession) +private fun KtEnumEntrySymbol.requiresSubClass(): Boolean { + return getDeclaredMemberScope().getAllSymbols().any { it !is KtConstructorSymbol } +} + internal fun KtSymbolWithVisibility.toPsiVisibilityForMember(): String = visibility.toPsiVisibilityForMember() internal fun KtSymbolWithVisibility.toPsiVisibilityForClass(isNested: Boolean): String = visibility.toPsiVisibilityForClass(isNested) diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassImpl.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassImpl.kt index 2a3cbdc391c..dd3e3c28dee 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassImpl.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassImpl.kt @@ -15,10 +15,14 @@ import org.jetbrains.kotlin.asJava.toLightClass import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap import org.jetbrains.kotlin.config.JvmDefaultMode import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.MemberDescriptor +import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.name.FqNameUnsafe import org.jetbrains.kotlin.psi.KtClassBody import org.jetbrains.kotlin.psi.KtClassOrObject +import org.jetbrains.kotlin.psi.KtConstructor +import org.jetbrains.kotlin.psi.KtEnumEntry import org.jetbrains.kotlin.resolve.DescriptorUtils // light class for top level or (inner/nested of top level) source declarations @@ -69,7 +73,7 @@ abstract class KtLightClassImpl( protected open fun computeIsFinal(): Boolean = when { classOrObject.hasModifier(KtTokens.FINAL_KEYWORD) -> true isAbstract() || isSealed() -> false - isEnum -> false + isEnum -> !hasEnumEntryWhichRequiresSubclass() !classOrObject.hasModifier(KtTokens.OPEN_KEYWORD) -> { val descriptor = lazy { getDescriptor() } var modifier = PsiModifier.FINAL @@ -83,7 +87,21 @@ abstract class KtLightClassImpl( else -> false } - private fun isAbstract(): Boolean = classOrObject.hasModifier(KtTokens.ABSTRACT_KEYWORD) || isInterface + private fun hasEnumEntryWhichRequiresSubclass(): Boolean { + return classOrObject.declarations.any { declaration -> + declaration is KtEnumEntry && declaration.declarations.any { it !is KtConstructor<*> } + } + } + + private fun isAbstract(): Boolean = + classOrObject.hasModifier(KtTokens.ABSTRACT_KEYWORD) || isInterface || (isEnum && hasAbstractMember()) + + private fun hasAbstractMember(): Boolean { + val descriptor = getDescriptor() ?: return false + return descriptor.unsubstitutedMemberScope.getContributedDescriptors().any { + (it as? MemberDescriptor)?.modality == Modality.ABSTRACT + } + } private fun isSealed(): Boolean = classOrObject.hasModifier(KtTokens.SEALED_KEYWORD) diff --git a/compiler/testData/asJava/lightClasses/lightClassByPsi/enums.fir.java b/compiler/testData/asJava/lightClasses/lightClassByPsi/enums.fir.java index 76325955366..75709976e3f 100644 --- a/compiler/testData/asJava/lightClasses/lightClassByPsi/enums.fir.java +++ b/compiler/testData/asJava/lightClasses/lightClassByPsi/enums.fir.java @@ -86,7 +86,7 @@ static final class PLUS /* IntArithmetics.PLUS*/ extends IntArithmetics { public int apply(int, int);// apply(int, int) } -public enum ProtocolState /* ProtocolState*/ { +public abstract enum ProtocolState /* ProtocolState*/ { WAITING { WAITING();// .ctor() diff --git a/compiler/testData/asJava/lightClasses/lightClassByPsi/enums.java b/compiler/testData/asJava/lightClasses/lightClassByPsi/enums.java index 33be058ba92..17ebd19365b 100644 --- a/compiler/testData/asJava/lightClasses/lightClassByPsi/enums.java +++ b/compiler/testData/asJava/lightClasses/lightClassByPsi/enums.java @@ -43,7 +43,7 @@ public enum Direction /* Direction*/ { private Direction();// .ctor() } -public enum IntArithmetics /* IntArithmetics*/ implements java.util.function.BinaryOperator, java.util.function.IntBinaryOperator { +public abstract enum IntArithmetics /* IntArithmetics*/ implements java.util.function.BinaryOperator, java.util.function.IntBinaryOperator { PLUS { PLUS();// .ctor() @@ -79,7 +79,7 @@ public static final class PLUS /* IntArithmetics.PLUS*/ extends IntArithmetics { public java.lang.Integer apply(int, int);// apply(int, int) } -public enum ProtocolState /* ProtocolState*/ { +public abstract enum ProtocolState /* ProtocolState*/ { WAITING { WAITING();// .ctor() diff --git a/compiler/tests/org/jetbrains/kotlin/asJava/KotlinLightClassStructureTest.java b/compiler/tests/org/jetbrains/kotlin/asJava/KotlinLightClassStructureTest.java index a000b24ced0..db5ac26c066 100644 --- a/compiler/tests/org/jetbrains/kotlin/asJava/KotlinLightClassStructureTest.java +++ b/compiler/tests/org/jetbrains/kotlin/asJava/KotlinLightClassStructureTest.java @@ -75,7 +75,7 @@ public abstract class KotlinLightClassStructureTest extends KotlinAsJavaTestBase } public void testEnum() { - checkModifiers("test.Enum", PUBLIC, ENUM); + checkModifiers("test.Enum", PUBLIC, FINAL, ENUM); } public void testTrait() {