Support enums in ultra-light classes

There's no need to add "values"/"valueOf" methods for them
(see com.intellij.psi.impl.compiled.StubBuildingVisitor#visitMethod that ignores them too)

We already have tests that check enum entries/synthetic methods
are properly resolved in Java:
idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/*Enum*
This commit is contained in:
Denis Zharkov
2018-11-21 15:54:22 +03:00
parent d5a640b82d
commit 206466f6ce
4 changed files with 83 additions and 20 deletions
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
import org.jetbrains.kotlin.asJava.builder.LightClassData import org.jetbrains.kotlin.asJava.builder.LightClassData
import org.jetbrains.kotlin.asJava.builder.LightMemberOriginForDeclaration import org.jetbrains.kotlin.asJava.builder.LightMemberOriginForDeclaration
import org.jetbrains.kotlin.asJava.elements.* import org.jetbrains.kotlin.asJava.elements.*
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.codegen.FunctionCodegen import org.jetbrains.kotlin.codegen.FunctionCodegen
import org.jetbrains.kotlin.codegen.JvmCodegenUtil import org.jetbrains.kotlin.codegen.JvmCodegenUtil
import org.jetbrains.kotlin.codegen.PropertyCodegen import org.jetbrains.kotlin.codegen.PropertyCodegen
@@ -90,11 +91,24 @@ class KtUltraLightClass(classOrObject: KtClassOrObject, private val support: Ult
PsiReferenceList.Role.EXTENDS_LIST PsiReferenceList.Role.EXTENDS_LIST
).also { list -> ).also { list ->
allSuperTypes() allSuperTypes()
.filter { (isInterface || !JvmCodegenUtil.isJvmInterface(it)) && !it.isAnyOrNullableAny() } .filter(this::isTypeForExtendsList)
.map(this::mapSupertype) .map(this::mapSupertype)
.forEach(list::addReference) .forEach(list::addReference)
} }
private fun isTypeForExtendsList(supertype: KotlinType): Boolean {
// Do not add redundant "extends java.lang.Object" anywhere
if (supertype.isAnyOrNullableAny()) return false
// We don't have Enum among enums supertype in sources neither we do for decompiled class-files and light-classes
if (isEnum && KotlinBuiltIns.isEnum(supertype)) return false
// Interfaces have only extends lists
if (isInterface) return true
return !JvmCodegenUtil.isJvmInterface(supertype)
}
override fun createImplementsList(): PsiReferenceList? = override fun createImplementsList(): PsiReferenceList? =
if (tooComplex) super.createImplementsList() if (tooComplex) super.createImplementsList()
else KotlinLightReferenceListBuilder( else KotlinLightReferenceListBuilder(
@@ -189,6 +203,18 @@ class KtUltraLightClass(classOrObject: KtClassOrObject, private val support: Ult
) )
} }
if (isEnum) {
for (ktEnumEntry in classOrObject.declarations.filterIsInstance<KtEnumEntry>()) {
val name = ktEnumEntry.name ?: continue
result.add(
KtUltraLightEnumEntry(
ktEnumEntry, name, this, support,
setOf(PsiModifier.STATIC, PsiModifier.FINAL, PsiModifier.PUBLIC)
)
)
}
}
result result
} }
@@ -268,7 +294,7 @@ class KtUltraLightClass(classOrObject: KtClassOrObject, private val support: Ult
result.add(defaultConstructor()) result.add(defaultConstructor())
} }
for (constructor in constructors.filterNot { isHiddenByDeprecation(it) }) { for (constructor in constructors.filterNot { isHiddenByDeprecation(it) }) {
result.addAll(asJavaMethods(constructor, false)) result.addAll(asJavaMethods(constructor, false, forcePrivate = isEnum))
} }
val primary = classOrObject.primaryConstructor val primary = classOrObject.primaryConstructor
if (primary != null && shouldGenerateNoArgOverload(primary)) { if (primary != null && shouldGenerateNoArgOverload(primary)) {
@@ -279,7 +305,7 @@ class KtUltraLightClass(classOrObject: KtClassOrObject, private val support: Ult
private fun shouldGenerateNoArgOverload(primary: KtPrimaryConstructor): Boolean { private fun shouldGenerateNoArgOverload(primary: KtPrimaryConstructor): Boolean {
return !primary.hasModifier(PRIVATE_KEYWORD) && return !primary.hasModifier(PRIVATE_KEYWORD) &&
!classOrObject.hasModifier(INNER_KEYWORD) && !classOrObject.hasModifier(INNER_KEYWORD) && !isEnum &&
primary.valueParameters.isNotEmpty() && primary.valueParameters.isNotEmpty() &&
primary.valueParameters.all { it.defaultValue != null } && primary.valueParameters.all { it.defaultValue != null } &&
classOrObject.allConstructors.none { it.valueParameters.isEmpty() } classOrObject.allConstructors.none { it.valueParameters.isEmpty() }
@@ -287,7 +313,7 @@ class KtUltraLightClass(classOrObject: KtClassOrObject, private val support: Ult
private fun defaultConstructor(): KtUltraLightMethod { private fun defaultConstructor(): KtUltraLightMethod {
val visibility = val visibility =
if (classOrObject is KtObjectDeclaration || classOrObject.hasModifier(SEALED_KEYWORD)) PsiModifier.PRIVATE if (classOrObject is KtObjectDeclaration || classOrObject.hasModifier(SEALED_KEYWORD) || isEnum) PsiModifier.PRIVATE
else PsiModifier.PUBLIC else PsiModifier.PUBLIC
return noArgConstructor(visibility, classOrObject) return noArgConstructor(visibility, classOrObject)
} }
@@ -314,31 +340,36 @@ class KtUltraLightClass(classOrObject: KtClassOrObject, private val support: Ult
override fun getOwnMethods(): List<KtLightMethod> = if (tooComplex) super.getOwnMethods() else _ownMethods override fun getOwnMethods(): List<KtLightMethod> = if (tooComplex) super.getOwnMethods() else _ownMethods
private fun asJavaMethods(ktFunction: KtFunction, forceStatic: Boolean): Collection<KtLightMethod> { private fun asJavaMethods(ktFunction: KtFunction, forceStatic: Boolean, forcePrivate: Boolean = false): Collection<KtLightMethod> {
if (ktFunction.hasAnnotation(JVM_SYNTHETIC_ANNOTATION_FQ_NAME)) return emptyList() if (ktFunction.hasAnnotation(JVM_SYNTHETIC_ANNOTATION_FQ_NAME)) return emptyList()
val basicMethod = asJavaMethod(ktFunction, forceStatic) val basicMethod = asJavaMethod(ktFunction, forceStatic, forcePrivate)
if (!ktFunction.hasAnnotation(JVM_OVERLOADS_FQ_NAME)) return listOf(basicMethod) if (!ktFunction.hasAnnotation(JVM_OVERLOADS_FQ_NAME)) return listOf(basicMethod)
val result = mutableListOf<KtLightMethod>() val result = mutableListOf<KtLightMethod>()
val numberOfDefaultParameters = ktFunction.valueParameters.count(KtParameter::hasDefaultValue) val numberOfDefaultParameters = ktFunction.valueParameters.count(KtParameter::hasDefaultValue)
for (numberOfDefaultParametersToAdd in 0 until numberOfDefaultParameters) { for (numberOfDefaultParametersToAdd in 0 until numberOfDefaultParameters) {
result.add(asJavaMethod(ktFunction, forceStatic, numberOfDefaultParametersToAdd)) result.add(asJavaMethod(ktFunction, forceStatic, forcePrivate, numberOfDefaultParametersToAdd))
} }
result.add(basicMethod) result.add(basicMethod)
return result return result
} }
private fun asJavaMethod(ktFunction: KtFunction, forceStatic: Boolean, numberOfDefaultParametersToAdd: Int = -1): KtLightMethod { private fun asJavaMethod(
ktFunction: KtFunction,
forceStatic: Boolean,
forcePrivate: Boolean,
numberOfDefaultParametersToAdd: Int = -1
): KtLightMethod {
val isConstructor = ktFunction is KtConstructor<*> val isConstructor = ktFunction is KtConstructor<*>
val name = val name =
if (isConstructor) if (isConstructor)
this.name this.name
else computeMethodName(ktFunction, ktFunction.name ?: SpecialNames.NO_NAME_PROVIDED.asString()) else computeMethodName(ktFunction, ktFunction.name ?: SpecialNames.NO_NAME_PROVIDED.asString())
val method = lightMethod(name.orEmpty(), ktFunction, forceStatic) val method = lightMethod(name.orEmpty(), ktFunction, forceStatic, forcePrivate)
val wrapper = KtUltraLightMethod(method, ktFunction, support, this) val wrapper = KtUltraLightMethod(method, ktFunction, support, this)
addReceiverParameter(ktFunction, wrapper) addReceiverParameter(ktFunction, wrapper)
@@ -384,7 +415,12 @@ class KtUltraLightClass(classOrObject: KtClassOrObject, private val support: Ult
private fun DeclarationDescriptor.getterIfProperty() = private fun DeclarationDescriptor.getterIfProperty() =
if (this@getterIfProperty is PropertyDescriptor) this@getterIfProperty.getter else this@getterIfProperty if (this@getterIfProperty is PropertyDescriptor) this@getterIfProperty.getter else this@getterIfProperty
private fun lightMethod(name: String, declaration: KtDeclaration, forceStatic: Boolean): LightMethodBuilder { private fun lightMethod(
name: String,
declaration: KtDeclaration,
forceStatic: Boolean,
forcePrivate: Boolean = false
): LightMethodBuilder {
val accessedProperty = if (declaration is KtPropertyAccessor) declaration.property else null val accessedProperty = if (declaration is KtPropertyAccessor) declaration.property else null
val outer = accessedProperty ?: declaration val outer = accessedProperty ?: declaration
return LightMethodBuilder( return LightMethodBuilder(
@@ -393,7 +429,7 @@ class KtUltraLightClass(classOrObject: KtClassOrObject, private val support: Ult
object : LightModifierList(manager, language) { object : LightModifierList(manager, language) {
override fun hasModifierProperty(name: String): Boolean { override fun hasModifierProperty(name: String): Boolean {
if (name == PsiModifier.PUBLIC || name == PsiModifier.PROTECTED || name == PsiModifier.PRIVATE) { if (name == PsiModifier.PUBLIC || name == PsiModifier.PROTECTED || name == PsiModifier.PRIVATE) {
if (declaration.isPrivate() || accessedProperty?.isPrivate() == true) { if (forcePrivate || declaration.isPrivate() || accessedProperty?.isPrivate() == true) {
return name == PsiModifier.PRIVATE return name == PsiModifier.PRIVATE
} }
if (declaration.hasModifier(PROTECTED_KEYWORD) || accessedProperty?.hasModifier(PROTECTED_KEYWORD) == true) { if (declaration.hasModifier(PROTECTED_KEYWORD) || accessedProperty?.hasModifier(PROTECTED_KEYWORD) == true) {
@@ -550,7 +586,7 @@ class KtUltraLightClass(classOrObject: KtClassOrObject, private val support: Ult
override fun copy(): KtLightClassImpl = KtUltraLightClass(classOrObject.copy() as KtClassOrObject, support) override fun copy(): KtLightClassImpl = KtUltraLightClass(classOrObject.copy() as KtClassOrObject, support)
} }
private class KtUltraLightField( private open class KtUltraLightField(
private val declaration: KtNamedDeclaration, private val declaration: KtNamedDeclaration,
name: String, name: String,
private val containingClass: KtUltraLightClass, private val containingClass: KtUltraLightClass,
@@ -600,6 +636,11 @@ private class KtUltraLightField(
declaration is KtObjectDeclaration -> declaration is KtObjectDeclaration ->
KtLightClassForSourceDeclaration.create(declaration)?.let { JavaPsiFacade.getElementFactory(project).createType(it) } KtLightClassForSourceDeclaration.create(declaration)?.let { JavaPsiFacade.getElementFactory(project).createType(it) }
?: nonExistent() ?: nonExistent()
declaration is KtEnumEntry -> {
(containingClass.kotlinOrigin.resolve() as? ClassDescriptor)
?.defaultType?.asPsiType(support, TypeMappingMode.DEFAULT, this)
?: nonExistent()
}
else -> { else -> {
val kotlinType = declaration.getKotlinType() ?: return@lazyPub PsiType.NULL val kotlinType = declaration.getKotlinType() ?: return@lazyPub PsiType.NULL
val descriptor = propertyDescriptor ?: return@lazyPub PsiType.NULL val descriptor = propertyDescriptor ?: return@lazyPub PsiType.NULL
@@ -640,6 +681,27 @@ private class KtUltraLightField(
} }
private class KtUltraLightEnumEntry(
declaration: KtNamedDeclaration,
name: String,
containingClass: KtUltraLightClass,
support: UltraLightSupport,
modifiers: Set<String>
) : KtUltraLightField(declaration, name, containingClass, support, modifiers), PsiEnumConstant {
override fun getInitializingClass(): PsiEnumConstantInitializer? = null
override fun getOrCreateInitializingClass(): PsiEnumConstantInitializer =
error("cannot create initializing class in light enum constant")
override fun getArgumentList(): PsiExpressionList? = null
override fun resolveMethod(): PsiMethod? = null
override fun resolveConstructor(): PsiMethod? = null
override fun resolveMethodGenerics(): JavaResolveResult = JavaResolveResult.EMPTY
override fun hasInitializer() = true
override fun computeConstantValue(visitedVars: MutableSet<PsiVariable>?) = this
}
internal class KtUltraLightMethod( internal class KtUltraLightMethod(
internal val delegate: LightMethodBuilder, internal val delegate: LightMethodBuilder,
originalElement: KtDeclaration, originalElement: KtDeclaration,
+5 -7
View File
@@ -1,19 +1,18 @@
import java.util.function.* import java.util.function.*
/** should load cls */
enum class Direction { enum class Direction {
NORTH, SOUTH, WEST, EAST NORTH, SOUTH, WEST, EAST
} }
/** should load cls */ enum class Color(val rgb: Int = 5) {
enum class Color(val rgb: Int) {
RED(0xFF0000), RED(0xFF0000),
GREEN(0x00FF00), GREEN(0x00FF00),
BLUE(0x0000FF) BLUE("0x0000FF");
constructor(y: String) : this(y.toInt())
} }
/** should load cls */
enum class ProtocolState { enum class ProtocolState {
WAITING { WAITING {
override fun signal() = TALKING override fun signal() = TALKING
@@ -26,7 +25,6 @@ enum class ProtocolState {
abstract fun signal(): ProtocolState abstract fun signal(): ProtocolState
} }
/** should load cls */
enum class IntArithmetics : BinaryOperator<Int>, IntBinaryOperator { enum class IntArithmetics : BinaryOperator<Int>, IntBinaryOperator {
PLUS { PLUS {
override fun apply(t: Int, u: Int): Int = t + u override fun apply(t: Int, u: Int): Int = t + u
@@ -40,4 +38,4 @@ enum class IntArithmetics : BinaryOperator<Int>, IntBinaryOperator {
class C { class C {
val enumConst: Direction? = Direction.EAST val enumConst: Direction? = Direction.EAST
} }
@@ -1105,6 +1105,10 @@ public abstract class KotlinBuiltIns {
return classFqNameEquals(descriptor, FQ_NAMES._enum); return classFqNameEquals(descriptor, FQ_NAMES._enum);
} }
public static boolean isEnum(@NotNull KotlinType type) {
return isConstructedFromGivenClassAndNotNullable(type, FQ_NAMES._enum);
}
public static boolean isComparable(@NotNull ClassDescriptor descriptor) { public static boolean isComparable(@NotNull ClassDescriptor descriptor) {
return classFqNameEquals(descriptor, FQ_NAMES.comparable.toUnsafe()); return classFqNameEquals(descriptor, FQ_NAMES.comparable.toUnsafe());
} }
@@ -109,7 +109,6 @@ class IDELightClassGenerationSupport(private val project: Project) : LightClassG
declaration.hasModifier(KtTokens.ANNOTATION_KEYWORD) || declaration.hasModifier(KtTokens.ANNOTATION_KEYWORD) ||
declaration.hasModifier(KtTokens.INLINE_KEYWORD) && declaration is KtClassOrObject || declaration.hasModifier(KtTokens.INLINE_KEYWORD) && declaration is KtClassOrObject ||
declaration.hasModifier(KtTokens.DATA_KEYWORD) || declaration.hasModifier(KtTokens.DATA_KEYWORD) ||
declaration.hasModifier(KtTokens.ENUM_KEYWORD) ||
declaration.hasModifier(KtTokens.SUSPEND_KEYWORD) declaration.hasModifier(KtTokens.SUSPEND_KEYWORD)
) { ) {
return declaration return declaration