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
This commit is contained in:
Jinseong Jeon
2023-06-15 19:23:22 -07:00
committed by Ilya Kirillov
parent 7930885131
commit 7f1424737e
7 changed files with 55 additions and 11 deletions
@@ -142,7 +142,10 @@ abstract class SymbolLightClassForNamedClassLike : SymbolLightClassForClassLike<
GranularModifiersBox.computeVisibilityForClass(ktModule, classOrObjectSymbolPointer, isTopLevel) GranularModifiersBox.computeVisibilityForClass(ktModule, classOrObjectSymbolPointer, isTopLevel)
} }
in GranularModifiersBox.MODALITY_MODIFIERS -> GranularModifiersBox.computeSimpleModality(ktModule, classOrObjectSymbolPointer) in GranularModifiersBox.MODALITY_MODIFIERS -> {
GranularModifiersBox.computeSimpleModality(ktModule, classOrObjectSymbolPointer)
}
PsiModifier.STATIC -> { PsiModifier.STATIC -> {
val isStatic = !isTopLevel && !isInner val isStatic = !isTopLevel && !isInner
mapOf(modifier to isStatic) mapOf(modifier to isStatic)
@@ -8,10 +8,13 @@ package org.jetbrains.kotlin.light.classes.symbol.modifierLists
import com.intellij.psi.PsiModifier import com.intellij.psi.PsiModifier
import kotlinx.collections.immutable.PersistentMap import kotlinx.collections.immutable.PersistentMap
import kotlinx.collections.immutable.toPersistentHashMap 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.KtSymbolWithModality
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithVisibility import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithVisibility
import org.jetbrains.kotlin.analysis.api.symbols.pointers.KtSymbolPointer import org.jetbrains.kotlin.analysis.api.symbols.pointers.KtSymbolPointer
import org.jetbrains.kotlin.analysis.project.structure.KtModule 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.computeSimpleModality
import org.jetbrains.kotlin.light.classes.symbol.toPsiVisibilityForClass import org.jetbrains.kotlin.light.classes.symbol.toPsiVisibilityForClass
import org.jetbrains.kotlin.light.classes.symbol.toPsiVisibilityForMember import org.jetbrains.kotlin.light.classes.symbol.toPsiVisibilityForMember
@@ -89,7 +92,11 @@ internal class GranularModifiersBox(
declarationPointer: KtSymbolPointer<KtSymbolWithModality>, declarationPointer: KtSymbolPointer<KtSymbolWithModality>,
): PersistentMap<String, Boolean> { ): PersistentMap<String, Boolean> {
val modality = declarationPointer.withSymbol(ktModule) { 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) return MODALITY_MODIFIERS_MAP.with(modality)
@@ -15,9 +15,7 @@ import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.annotations.* import org.jetbrains.kotlin.analysis.api.annotations.*
import org.jetbrains.kotlin.analysis.api.base.KtConstantValue import org.jetbrains.kotlin.analysis.api.base.KtConstantValue
import org.jetbrains.kotlin.analysis.api.components.DefaultTypeClassIds import org.jetbrains.kotlin.analysis.api.components.DefaultTypeClassIds
import org.jetbrains.kotlin.analysis.api.symbols.KtKotlinPropertySymbol import org.jetbrains.kotlin.analysis.api.symbols.*
import org.jetbrains.kotlin.analysis.api.symbols.KtPropertySymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithModality 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.KtSymbolWithTypeParameters
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithVisibility import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithVisibility
@@ -78,6 +76,24 @@ internal fun KtSymbolWithModality.computeSimpleModality(): String? = when (modal
Modality.OPEN -> null 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.toPsiVisibilityForMember(): String = visibility.toPsiVisibilityForMember()
internal fun KtSymbolWithVisibility.toPsiVisibilityForClass(isNested: Boolean): String = visibility.toPsiVisibilityForClass(isNested) internal fun KtSymbolWithVisibility.toPsiVisibilityForClass(isNested: Boolean): String = visibility.toPsiVisibilityForClass(isNested)
@@ -15,10 +15,14 @@ import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
import org.jetbrains.kotlin.config.JvmDefaultMode import org.jetbrains.kotlin.config.JvmDefaultMode
import org.jetbrains.kotlin.descriptors.ClassDescriptor 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.lexer.KtTokens
import org.jetbrains.kotlin.name.FqNameUnsafe import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.psi.KtClassBody import org.jetbrains.kotlin.psi.KtClassBody
import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtConstructor
import org.jetbrains.kotlin.psi.KtEnumEntry
import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils
// light class for top level or (inner/nested of top level) source declarations // 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 { protected open fun computeIsFinal(): Boolean = when {
classOrObject.hasModifier(KtTokens.FINAL_KEYWORD) -> true classOrObject.hasModifier(KtTokens.FINAL_KEYWORD) -> true
isAbstract() || isSealed() -> false isAbstract() || isSealed() -> false
isEnum -> false isEnum -> !hasEnumEntryWhichRequiresSubclass()
!classOrObject.hasModifier(KtTokens.OPEN_KEYWORD) -> { !classOrObject.hasModifier(KtTokens.OPEN_KEYWORD) -> {
val descriptor = lazy { getDescriptor() } val descriptor = lazy { getDescriptor() }
var modifier = PsiModifier.FINAL var modifier = PsiModifier.FINAL
@@ -83,7 +87,21 @@ abstract class KtLightClassImpl(
else -> false 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) private fun isSealed(): Boolean = classOrObject.hasModifier(KtTokens.SEALED_KEYWORD)
@@ -86,7 +86,7 @@ static final class PLUS /* IntArithmetics.PLUS*/ extends IntArithmetics {
public int apply(int, int);// apply(int, int) public int apply(int, int);// apply(int, int)
} }
public enum ProtocolState /* ProtocolState*/ { public abstract enum ProtocolState /* ProtocolState*/ {
WAITING { WAITING {
WAITING();// .ctor() WAITING();// .ctor()
@@ -43,7 +43,7 @@ public enum Direction /* Direction*/ {
private Direction();// .ctor() private Direction();// .ctor()
} }
public enum IntArithmetics /* IntArithmetics*/ implements java.util.function.BinaryOperator<java.lang.Integer>, java.util.function.IntBinaryOperator { public abstract enum IntArithmetics /* IntArithmetics*/ implements java.util.function.BinaryOperator<java.lang.Integer>, java.util.function.IntBinaryOperator {
PLUS { PLUS {
PLUS();// .ctor() 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 java.lang.Integer apply(int, int);// apply(int, int)
} }
public enum ProtocolState /* ProtocolState*/ { public abstract enum ProtocolState /* ProtocolState*/ {
WAITING { WAITING {
WAITING();// .ctor() WAITING();// .ctor()
@@ -75,7 +75,7 @@ public abstract class KotlinLightClassStructureTest extends KotlinAsJavaTestBase
} }
public void testEnum() { public void testEnum() {
checkModifiers("test.Enum", PUBLIC, ENUM); checkModifiers("test.Enum", PUBLIC, FINAL, ENUM);
} }
public void testTrait() { public void testTrait() {