From 206466f6ce6ab23c1ede4ef7beb05ee8672479dd Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Wed, 21 Nov 2018 15:54:22 +0300 Subject: [PATCH] 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* --- .../kotlin/asJava/classes/ultraLightPsi.kt | 86 ++++++++++++++++--- .../asJava/ultraLightClasses/enums.kt | 12 ++- .../kotlin/builtins/KotlinBuiltIns.java | 4 + .../resolve/IDELightClassGenerationSupport.kt | 1 - 4 files changed, 83 insertions(+), 20 deletions(-) diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightPsi.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightPsi.kt index 937cc81d924..8614381b873 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightPsi.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightPsi.kt @@ -19,6 +19,7 @@ import org.jetbrains.kotlin.asJava.LightClassGenerationSupport import org.jetbrains.kotlin.asJava.builder.LightClassData import org.jetbrains.kotlin.asJava.builder.LightMemberOriginForDeclaration import org.jetbrains.kotlin.asJava.elements.* +import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.codegen.FunctionCodegen import org.jetbrains.kotlin.codegen.JvmCodegenUtil import org.jetbrains.kotlin.codegen.PropertyCodegen @@ -90,11 +91,24 @@ class KtUltraLightClass(classOrObject: KtClassOrObject, private val support: Ult PsiReferenceList.Role.EXTENDS_LIST ).also { list -> allSuperTypes() - .filter { (isInterface || !JvmCodegenUtil.isJvmInterface(it)) && !it.isAnyOrNullableAny() } + .filter(this::isTypeForExtendsList) .map(this::mapSupertype) .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? = if (tooComplex) super.createImplementsList() else KotlinLightReferenceListBuilder( @@ -189,6 +203,18 @@ class KtUltraLightClass(classOrObject: KtClassOrObject, private val support: Ult ) } + if (isEnum) { + for (ktEnumEntry in classOrObject.declarations.filterIsInstance()) { + val name = ktEnumEntry.name ?: continue + result.add( + KtUltraLightEnumEntry( + ktEnumEntry, name, this, support, + setOf(PsiModifier.STATIC, PsiModifier.FINAL, PsiModifier.PUBLIC) + ) + ) + } + } + result } @@ -268,7 +294,7 @@ class KtUltraLightClass(classOrObject: KtClassOrObject, private val support: Ult result.add(defaultConstructor()) } for (constructor in constructors.filterNot { isHiddenByDeprecation(it) }) { - result.addAll(asJavaMethods(constructor, false)) + result.addAll(asJavaMethods(constructor, false, forcePrivate = isEnum)) } val primary = classOrObject.primaryConstructor if (primary != null && shouldGenerateNoArgOverload(primary)) { @@ -279,7 +305,7 @@ class KtUltraLightClass(classOrObject: KtClassOrObject, private val support: Ult private fun shouldGenerateNoArgOverload(primary: KtPrimaryConstructor): Boolean { return !primary.hasModifier(PRIVATE_KEYWORD) && - !classOrObject.hasModifier(INNER_KEYWORD) && + !classOrObject.hasModifier(INNER_KEYWORD) && !isEnum && primary.valueParameters.isNotEmpty() && primary.valueParameters.all { it.defaultValue != null } && classOrObject.allConstructors.none { it.valueParameters.isEmpty() } @@ -287,7 +313,7 @@ class KtUltraLightClass(classOrObject: KtClassOrObject, private val support: Ult private fun defaultConstructor(): KtUltraLightMethod { 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 return noArgConstructor(visibility, classOrObject) } @@ -314,31 +340,36 @@ class KtUltraLightClass(classOrObject: KtClassOrObject, private val support: Ult override fun getOwnMethods(): List = if (tooComplex) super.getOwnMethods() else _ownMethods - private fun asJavaMethods(ktFunction: KtFunction, forceStatic: Boolean): Collection { + private fun asJavaMethods(ktFunction: KtFunction, forceStatic: Boolean, forcePrivate: Boolean = false): Collection { 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) val result = mutableListOf() val numberOfDefaultParameters = ktFunction.valueParameters.count(KtParameter::hasDefaultValue) for (numberOfDefaultParametersToAdd in 0 until numberOfDefaultParameters) { - result.add(asJavaMethod(ktFunction, forceStatic, numberOfDefaultParametersToAdd)) + result.add(asJavaMethod(ktFunction, forceStatic, forcePrivate, numberOfDefaultParametersToAdd)) } result.add(basicMethod) 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 name = if (isConstructor) this.name 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) addReceiverParameter(ktFunction, wrapper) @@ -384,7 +415,12 @@ class KtUltraLightClass(classOrObject: KtClassOrObject, private val support: Ult private fun DeclarationDescriptor.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 outer = accessedProperty ?: declaration return LightMethodBuilder( @@ -393,7 +429,7 @@ class KtUltraLightClass(classOrObject: KtClassOrObject, private val support: Ult object : LightModifierList(manager, language) { override fun hasModifierProperty(name: String): Boolean { 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 } 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) } -private class KtUltraLightField( +private open class KtUltraLightField( private val declaration: KtNamedDeclaration, name: String, private val containingClass: KtUltraLightClass, @@ -600,6 +636,11 @@ private class KtUltraLightField( declaration is KtObjectDeclaration -> KtLightClassForSourceDeclaration.create(declaration)?.let { JavaPsiFacade.getElementFactory(project).createType(it) } ?: nonExistent() + declaration is KtEnumEntry -> { + (containingClass.kotlinOrigin.resolve() as? ClassDescriptor) + ?.defaultType?.asPsiType(support, TypeMappingMode.DEFAULT, this) + ?: nonExistent() + } else -> { val kotlinType = declaration.getKotlinType() ?: 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 +) : 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?) = this +} + internal class KtUltraLightMethod( internal val delegate: LightMethodBuilder, originalElement: KtDeclaration, diff --git a/compiler/testData/asJava/ultraLightClasses/enums.kt b/compiler/testData/asJava/ultraLightClasses/enums.kt index e856b5721e5..8373de7f476 100644 --- a/compiler/testData/asJava/ultraLightClasses/enums.kt +++ b/compiler/testData/asJava/ultraLightClasses/enums.kt @@ -1,19 +1,18 @@ import java.util.function.* -/** should load cls */ enum class Direction { NORTH, SOUTH, WEST, EAST } -/** should load cls */ -enum class Color(val rgb: Int) { +enum class Color(val rgb: Int = 5) { RED(0xFF0000), GREEN(0x00FF00), - BLUE(0x0000FF) + BLUE("0x0000FF"); + + constructor(y: String) : this(y.toInt()) } -/** should load cls */ enum class ProtocolState { WAITING { override fun signal() = TALKING @@ -26,7 +25,6 @@ enum class ProtocolState { abstract fun signal(): ProtocolState } -/** should load cls */ enum class IntArithmetics : BinaryOperator, IntBinaryOperator { PLUS { override fun apply(t: Int, u: Int): Int = t + u @@ -40,4 +38,4 @@ enum class IntArithmetics : BinaryOperator, IntBinaryOperator { class C { val enumConst: Direction? = Direction.EAST -} \ No newline at end of file +} diff --git a/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java b/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java index 408cbe3393c..eb62082ec15 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java +++ b/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java @@ -1105,6 +1105,10 @@ public abstract class KotlinBuiltIns { 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) { return classFqNameEquals(descriptor, FQ_NAMES.comparable.toUnsafe()); } diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.kt index d54868fa341..2a88d0367d7 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.kt @@ -109,7 +109,6 @@ class IDELightClassGenerationSupport(private val project: Project) : LightClassG declaration.hasModifier(KtTokens.ANNOTATION_KEYWORD) || declaration.hasModifier(KtTokens.INLINE_KEYWORD) && declaration is KtClassOrObject || declaration.hasModifier(KtTokens.DATA_KEYWORD) || - declaration.hasModifier(KtTokens.ENUM_KEYWORD) || declaration.hasModifier(KtTokens.SUSPEND_KEYWORD) ) { return declaration