diff --git a/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/CodegenUtil.java b/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/CodegenUtil.java index 710ee873476..1245d6639a2 100644 --- a/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/CodegenUtil.java +++ b/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/CodegenUtil.java @@ -176,10 +176,8 @@ public class CodegenUtil { && JetTypeChecker.DEFAULT.isSubtypeOf(methodTypeParameters.get(0).getType(), nullableString); } - public static boolean isEnumValuesMethod(@NotNull FunctionDescriptor functionDescriptor) { - List methodTypeParameters = functionDescriptor.getValueParameters(); - return DescriptorUtils.ENUM_VALUES.equals(functionDescriptor.getName()) - && methodTypeParameters.isEmpty(); + public static boolean isEnumValuesProperty(@NotNull VariableDescriptor propertyDescriptor) { + return DescriptorUtils.ENUM_VALUES.equals(propertyDescriptor.getName()); } @Nullable diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java index fe514316a60..6712fa717c0 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java @@ -795,14 +795,14 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { private void generateEnumValuesMethod() { Type type = typeMapper.mapType(DescriptorUtilsKt.getBuiltIns(descriptor).getArrayType(INVARIANT, descriptor.getDefaultType())); - FunctionDescriptor valuesFunction = - CollectionsKt.single(descriptor.getStaticScope().getFunctions(ENUM_VALUES, NoLookupLocation.FROM_BACKEND), new Function1() { + VariableDescriptor valuesProperty = + CollectionsKt.single(descriptor.getStaticScope().getProperties(ENUM_VALUES, NoLookupLocation.FROM_BACKEND), new Function1() { @Override - public Boolean invoke(FunctionDescriptor descriptor) { - return CodegenUtil.isEnumValuesMethod(descriptor); + public Boolean invoke(VariableDescriptor descriptor) { + return CodegenUtil.isEnumValuesProperty(descriptor); } }); - MethodVisitor mv = v.newMethod(JvmDeclarationOriginKt.OtherOrigin(myClass, valuesFunction), ACC_PUBLIC | ACC_STATIC, ENUM_VALUES.asString(), + MethodVisitor mv = v.newMethod(JvmDeclarationOriginKt.OtherOrigin(myClass, valuesProperty), ACC_PUBLIC | ACC_STATIC, ENUM_VALUES.asString(), "()" + type.getDescriptor(), null, null); if (state.getClassBuilderMode() != ClassBuilderMode.FULL) return; diff --git a/compiler/testData/builtin-classes.txt b/compiler/testData/builtin-classes.txt index 0fa231bef3f..c5709214ef4 100644 --- a/compiler/testData/builtin-classes.txt +++ b/compiler/testData/builtin-classes.txt @@ -315,8 +315,10 @@ public final enum class DeprecationLevel : kotlin.Enum public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: kotlin.DeprecationLevel): kotlin.Int // Static members + public final /*synthesized*/ val values: kotlin.Array + public final fun (): kotlin.Array public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): kotlin.DeprecationLevel - public final /*synthesized*/ fun values(): kotlin.Array + @kotlin.Deprecated(message = "Use 'values' property instead", replaceWith = kotlin.ReplaceWith(expression = "this.values", imports = {})) public final /*synthesized*/ fun values(): kotlin.Array } public final class Double : kotlin.Number, kotlin.Comparable { diff --git a/compiler/testData/diagnostics/tests/enum/classObjectInEnumPrivate.kt b/compiler/testData/diagnostics/tests/enum/classObjectInEnumPrivate.kt index c7c53eb48ef..171e9fed212 100644 --- a/compiler/testData/diagnostics/tests/enum/classObjectInEnumPrivate.kt +++ b/compiler/testData/diagnostics/tests/enum/classObjectInEnumPrivate.kt @@ -4,7 +4,7 @@ enum class E { private companion object } -fun foo() = E.values() +fun foo() = E.values fun bar() = E.valueOf("ENTRY") fun baz() = E.ENTRY fun quux() = E \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/enum/javaEnumValuesMethod.kt b/compiler/testData/diagnostics/tests/enum/javaEnumValuesMethod.kt index 6089eef7c3c..bcfc4e16b72 100644 --- a/compiler/testData/diagnostics/tests/enum/javaEnumValuesMethod.kt +++ b/compiler/testData/diagnostics/tests/enum/javaEnumValuesMethod.kt @@ -7,5 +7,5 @@ public enum A { // FILE: test.kt fun main() { - checkSubtype>(A.values()) + checkSubtype>(A.values) } diff --git a/compiler/testData/diagnostics/tests/enum/starImportNestedClassAndEntries.kt b/compiler/testData/diagnostics/tests/enum/starImportNestedClassAndEntries.kt index b2b9ba97aa3..0ddad0cdd2e 100644 --- a/compiler/testData/diagnostics/tests/enum/starImportNestedClassAndEntries.kt +++ b/compiler/testData/diagnostics/tests/enum/starImportNestedClassAndEntries.kt @@ -21,4 +21,4 @@ fun f1() = ENTRY fun f2() = ANOTHER fun f3() = Nested() fun f4() = Nested.foo() -fun f5() = values() +fun f5() = values diff --git a/compiler/testData/diagnostics/tests/enum/valuesValueOfAndEntriesAccessibility.kt b/compiler/testData/diagnostics/tests/enum/valuesValueOfAndEntriesAccessibility.kt index b3580c5fac2..ef36116d167 100644 --- a/compiler/testData/diagnostics/tests/enum/valuesValueOfAndEntriesAccessibility.kt +++ b/compiler/testData/diagnostics/tests/enum/valuesValueOfAndEntriesAccessibility.kt @@ -3,16 +3,16 @@ enum class E { companion object { fun foo(): E = ENTRY - fun bar(): Array = values() + fun bar(): Array = values fun baz(): E = valueOf("ENTRY") - val valuez = values() + val valuez = values } fun oof(): E = ENTRY - fun rab(): Array = values() + fun rab(): Array = values fun zab(): E = valueOf("ENTRY") } fun foo() = E.ENTRY -fun bar() = E.values() +fun bar() = E.values fun baz() = E.valueOf("ENTRY") diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaStaticClassScope.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaStaticClassScope.kt index 012dab52b6c..286b2ead9e8 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaStaticClassScope.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaStaticClassScope.kt @@ -30,6 +30,7 @@ import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.DescriptorFactory.createEnumValueOfMethod import org.jetbrains.kotlin.resolve.DescriptorFactory.createEnumValuesMethod +import org.jetbrains.kotlin.resolve.DescriptorFactory.createEnumValuesProperty import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter import org.jetbrains.kotlin.types.JetType @@ -61,7 +62,7 @@ public class LazyJavaStaticClassScope( } override fun getPropertyNames(kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean): Collection = - memberIndex().getAllFieldNames() + memberIndex().getAllFieldNames() + (if (jClass.isEnum) listOf(DescriptorUtils.ENUM_VALUES) else emptyList()) override fun getClassNames(kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean): Collection = listOf() override fun getClassifier(name: Name, location: LookupLocation): ClassifierDescriptor? = null @@ -99,6 +100,12 @@ public class LazyJavaStaticClassScope( } result.addAll(actualProperties) + + if (jClass.isEnum) { + if (name == DescriptorUtils.ENUM_VALUES) { + result.add(createEnumValuesProperty(getContainingDeclaration())) + } + } } override fun getContainingDeclaration() = super.getContainingDeclaration() as LazyJavaClassDescriptor diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/specialBuiltinMembers.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/specialBuiltinMembers.kt index d3ac7c3f7a4..ca0ab946e19 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/specialBuiltinMembers.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/specialBuiltinMembers.kt @@ -31,6 +31,7 @@ import org.jetbrains.kotlin.utils.addToStdlib.check import org.jetbrains.kotlin.load.java.BuiltinSpecialProperties.getBuiltinSpecialPropertyGetterName import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature.sameAsBuiltinMethodWithErasedValueParameters import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature.getSpecialSignatureInfo +import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe object BuiltinSpecialProperties { @@ -201,6 +202,13 @@ fun T.getOverriddenBuiltinWithDifferentJvmDescrip } fun getJvmMethodNameIfSpecial(callableMemberDescriptor: CallableMemberDescriptor): String? { + if (callableMemberDescriptor.propertyIfAccessor.name == DescriptorUtils.ENUM_VALUES) { + val containingDeclaration = callableMemberDescriptor.containingDeclaration + if (callableMemberDescriptor is PropertyAccessorDescriptor + && containingDeclaration is ClassDescriptor + && containingDeclaration.kind == ClassKind.ENUM_CLASS) return DescriptorUtils.ENUM_VALUES.asString() + } + val builtinOverridden = getBuiltinOverriddenThatAffectsJvmName(callableMemberDescriptor)?.propertyIfAccessor ?: return null return when (builtinOverridden) { diff --git a/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/AnnotationsImpl.kt b/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/AnnotationsImpl.kt index 5b4b852a341..4629cb885f6 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/AnnotationsImpl.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/AnnotationsImpl.kt @@ -64,5 +64,10 @@ public class AnnotationsImpl : Annotations { public fun create(annotationsWithTargets: List): AnnotationsImpl { return AnnotationsImpl(annotationsWithTargets, 0) } + + @JvmStatic + public fun createWithNoTarget(vararg annotations: AnnotationDescriptor): AnnotationsImpl { + return create(annotations.map { AnnotationWithTarget(it, null) }) + } } } diff --git a/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/annotationUtil.kt b/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/annotationUtil.kt index 8cb0c5da8a7..dbe702a4070 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/annotationUtil.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/annotationUtil.kt @@ -16,9 +16,44 @@ package org.jetbrains.kotlin.descriptors.annotations +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.SourceElement +import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.constants.AnnotationValue +import org.jetbrains.kotlin.resolve.constants.ArrayValue +import org.jetbrains.kotlin.resolve.constants.StringValue +import org.jetbrains.kotlin.types.Variance // Please synchronize this set with JetTokens.ANNOTATION_MODIFIERS_KEYWORDS_ARRAY public val ANNOTATION_MODIFIERS_FQ_NAMES: Set = arrayOf("data", "inline", "noinline", "tailrec", "external", "annotation.annotation", "crossinline").map { FqName("kotlin.$it") }.toSet() +public fun KotlinBuiltIns.createDeprecatedAnnotation(message: String, replaceWith: String): AnnotationDescriptor { + val deprecatedAnnotation = deprecatedAnnotation + val parameters = deprecatedAnnotation.unsubstitutedPrimaryConstructor!!.valueParameters + + val replaceWithClass = getBuiltInClassByName(Name.identifier("ReplaceWith")) + + val replaceWithParameters = replaceWithClass.unsubstitutedPrimaryConstructor!!.valueParameters + return AnnotationDescriptorImpl( + deprecatedAnnotation.defaultType, + mapOf( + parameters["message"] to StringValue(message, this), + parameters["replaceWith"] to AnnotationValue( + AnnotationDescriptorImpl( + replaceWithClass.defaultType, + mapOf( + replaceWithParameters["expression"] to StringValue(replaceWith, this), + replaceWithParameters["imports"] to ArrayValue( + emptyList(), getArrayType(Variance.INVARIANT, stringType), this) + ), + SourceElement.NO_SOURCE + ) + ) + ), + SourceElement.NO_SOURCE) +} + +private operator fun Collection.get(parameterName: String) = single { it.name.asString() == parameterName } diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorFactory.java b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorFactory.java index 023a7a330db..a5f4dc9924c 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorFactory.java +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorFactory.java @@ -19,7 +19,9 @@ package org.jetbrains.kotlin.resolve; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.descriptors.*; +import org.jetbrains.kotlin.descriptors.annotations.AnnotationUtilKt; import org.jetbrains.kotlin.descriptors.annotations.Annotations; +import org.jetbrains.kotlin.descriptors.annotations.AnnotationsImpl; import org.jetbrains.kotlin.descriptors.impl.*; import org.jetbrains.kotlin.name.Name; import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver; @@ -112,8 +114,12 @@ public class DescriptorFactory { @NotNull public static SimpleFunctionDescriptor createEnumValuesMethod(@NotNull ClassDescriptor enumClass) { + AnnotationsImpl annotations = AnnotationsImpl.createWithNoTarget( + AnnotationUtilKt.createDeprecatedAnnotation(getBuiltIns(enumClass), "Use 'values' property instead", "this.values") + ); + SimpleFunctionDescriptorImpl values = - SimpleFunctionDescriptorImpl.create(enumClass, Annotations.Companion.getEMPTY(), DescriptorUtils.ENUM_VALUES, + SimpleFunctionDescriptorImpl.create(enumClass, annotations, DescriptorUtils.ENUM_VALUES, CallableMemberDescriptor.Kind.SYNTHESIZED, enumClass.getSource()); return values.initialize(null, null, Collections.emptyList(), Collections.emptyList(), @@ -121,6 +127,26 @@ public class DescriptorFactory { Modality.FINAL, Visibilities.PUBLIC); } + @NotNull + public static PropertyDescriptor createEnumValuesProperty(@NotNull ClassDescriptor enumClass) { + PropertyDescriptorImpl values = + PropertyDescriptorImpl.create( + enumClass, Annotations.Companion.getEMPTY(), Modality.FINAL, Visibilities.PUBLIC, /* isVar */ false, + DescriptorUtils.ENUM_VALUES, CallableMemberDescriptor.Kind.SYNTHESIZED, enumClass.getSource(), + /* lateInit = */ false, /* isConst = */ false + ); + + JetType type = getBuiltIns(enumClass).getArrayType(Variance.INVARIANT, enumClass.getDefaultType()); + + PropertyGetterDescriptorImpl getter = createDefaultGetter(values, Annotations.Companion.getEMPTY()); + + values.initialize(getter, null); + getter.initialize(type); + values.setType(type, Collections.emptyList(), null, (JetType) null); + + return values; + } + @NotNull public static SimpleFunctionDescriptor createEnumValueOfMethod(@NotNull ClassDescriptor enumClass) { SimpleFunctionDescriptorImpl valueOf = diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/scopes/StaticScopeForKotlinClass.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/scopes/StaticScopeForKotlinClass.kt index e2ee0c92454..9af681d12bb 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/scopes/StaticScopeForKotlinClass.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/scopes/StaticScopeForKotlinClass.kt @@ -16,13 +16,12 @@ package org.jetbrains.kotlin.resolve.scopes -import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.ClassKind -import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.incremental.components.LookupLocation import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.DescriptorFactory.createEnumValueOfMethod import org.jetbrains.kotlin.resolve.DescriptorFactory.createEnumValuesMethod +import org.jetbrains.kotlin.resolve.DescriptorFactory.createEnumValuesProperty import org.jetbrains.kotlin.utils.Printer import java.util.ArrayList @@ -40,10 +39,21 @@ public class StaticScopeForKotlinClass( } } - override fun getDescriptors(kindFilter: DescriptorKindFilter, - nameFilter: (Name) -> Boolean) = functions + private val properties: List by lazy { + if (containingClass.kind != ClassKind.ENUM_CLASS) { + listOf() + } + else { + listOf(createEnumValuesProperty(containingClass)) + } + } - override fun getOwnDeclaredDescriptors() = functions + override fun getDescriptors(kindFilter: DescriptorKindFilter, + nameFilter: (Name) -> Boolean) = functions + properties + + override fun getOwnDeclaredDescriptors() = functions + properties + + override fun getProperties(name: Name, location: LookupLocation) = properties.filterTo(ArrayList(1)) { it.name == name } override fun getFunctions(name: Name, location: LookupLocation) = functions.filterTo(ArrayList(2)) { it.getName() == name } diff --git a/idea/testData/checker/ClassObjectInEnum.kt b/idea/testData/checker/ClassObjectInEnum.kt index b3580c5fac2..ef36116d167 100644 --- a/idea/testData/checker/ClassObjectInEnum.kt +++ b/idea/testData/checker/ClassObjectInEnum.kt @@ -3,16 +3,16 @@ enum class E { companion object { fun foo(): E = ENTRY - fun bar(): Array = values() + fun bar(): Array = values fun baz(): E = valueOf("ENTRY") - val valuez = values() + val valuez = values } fun oof(): E = ENTRY - fun rab(): Array = values() + fun rab(): Array = values fun zab(): E = valueOf("ENTRY") } fun foo() = E.ENTRY -fun bar() = E.values() +fun bar() = E.values fun baz() = E.valueOf("ENTRY") diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/enumValues.kt b/idea/testData/quickfix/deprecatedSymbolUsage/enumValues.kt new file mode 100644 index 00000000000..9e1638ae521 --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/enumValues.kt @@ -0,0 +1,7 @@ +// "Replace with 'this.values'" "true" + +enum class MyEnum {} + +fun foo() { + MyEnum.values() +} diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/enumValues.kt.after b/idea/testData/quickfix/deprecatedSymbolUsage/enumValues.kt.after new file mode 100644 index 00000000000..fcea08e59c2 --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/enumValues.kt.after @@ -0,0 +1,7 @@ +// "Replace with 'this.values'" "true" + +enum class MyEnum {} + +fun foo() { + MyEnum.values +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java index d0d8afedcbb..8998fa99c26 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -3082,6 +3082,12 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { doTest(fileName); } + @TestMetadata("enumValues.kt") + public void testEnumValues() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/enumValues.kt"); + doTest(fileName); + } + @TestMetadata("extensionForGenericClass.kt") public void testExtensionForGenericClass() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/extensionForGenericClass.kt");