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() {