Correct descriptor shape for @JvmRecord annotated classes

This commit adds relevant functions: hashCode, toString, equals
(if the class is not a data class)
And supertype j.l.Record

It only affects descriptor contents, i.e. works for FE

^KT-43677 In Progress
This commit is contained in:
Denis.Zharkov
2020-11-25 18:42:51 +03:00
parent ca2e199b53
commit bef50c0342
12 changed files with 193 additions and 29 deletions
@@ -0,0 +1,49 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.resolve.jvm
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
import org.jetbrains.kotlin.incremental.components.LookupLocation
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.AdditionalClassPartsProvider
import org.jetbrains.kotlin.resolve.FunctionsFromAny
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.descriptorUtil.resolveTopLevelClass
import org.jetbrains.kotlin.resolve.jvm.annotations.isJvmRecord
import org.jetbrains.kotlin.types.KotlinType
class JvmAdditionalClassPartsProvider : AdditionalClassPartsProvider {
override fun generateAdditionalMethods(
thisDescriptor: ClassDescriptor,
result: MutableCollection<SimpleFunctionDescriptor>,
name: Name,
location: LookupLocation,
fromSupertypes: Collection<SimpleFunctionDescriptor>
) {
if (thisDescriptor.isJvmRecord()) {
FunctionsFromAny.addFunctionFromAnyIfNeeded(thisDescriptor, result, name, fromSupertypes)
}
}
override fun getAdditionalSupertypes(
thisDescriptor: ClassDescriptor,
existingSupertypes: List<KotlinType>
): List<KotlinType> {
if (thisDescriptor.isJvmRecord() && existingSupertypes.none(::isJavaLangRecordType)) {
thisDescriptor.module.resolveTopLevelClass(JAVA_LANG_RECORD_FQ_NAME, NoLookupLocation.FOR_DEFAULT_IMPORTS)?.defaultType?.let {
return listOf(it)
}
}
return emptyList()
}
}
private fun isJavaLangRecordType(it: KotlinType) =
KotlinBuiltIns.isConstructedFromGivenClass(it, JAVA_LANG_RECORD_FQ_NAME)
@@ -110,6 +110,7 @@ object JvmPlatformConfigurator : PlatformConfiguratorBase(
container.useImpl<JvmPlatformOverloadsSpecificityComparator>()
container.useImpl<JvmDefaultSuperCallChecker>()
container.useImpl<JvmSamConversionOracle>()
container.useImpl<JvmAdditionalClassPartsProvider>()
container.useInstance(FunctionWithBigAritySupport.LanguageVersionDependent)
container.useInstance(GenericArrayClassLiteralSupport.Enabled)
container.useInstance(JavaActualAnnotationArgumentExtractor())
@@ -92,6 +92,7 @@ public class DescriptorResolver {
private final DeclarationReturnTypeSanitizer declarationReturnTypeSanitizer;
private final DataFlowValueFactory dataFlowValueFactory;
private final Iterable<DeclarationSignatureAnonymousTypeTransformer> anonymousTypeTransformers;
private final AdditionalClassPartsProvider additionalClassPartsProvider;
public DescriptorResolver(
@NotNull AnnotationResolver annotationResolver,
@@ -111,7 +112,8 @@ public class DescriptorResolver {
@NotNull TypeApproximator approximator,
@NotNull DeclarationReturnTypeSanitizer declarationReturnTypeSanitizer,
@NotNull DataFlowValueFactory dataFlowValueFactory,
@NotNull Iterable<DeclarationSignatureAnonymousTypeTransformer> anonymousTypeTransformers
@NotNull Iterable<DeclarationSignatureAnonymousTypeTransformer> anonymousTypeTransformers,
@NotNull AdditionalClassPartsProvider additionalClassPartsProvider
) {
this.annotationResolver = annotationResolver;
this.builtIns = builtIns;
@@ -131,6 +133,7 @@ public class DescriptorResolver {
this.declarationReturnTypeSanitizer = declarationReturnTypeSanitizer;
this.dataFlowValueFactory = dataFlowValueFactory;
this.anonymousTypeTransformers = anonymousTypeTransformers;
this.additionalClassPartsProvider = additionalClassPartsProvider;
}
public List<KotlinType> resolveSupertypes(
@@ -156,6 +159,7 @@ public class DescriptorResolver {
}
syntheticResolveExtension.addSyntheticSupertypes(classDescriptor, supertypes);
supertypes.addAll(additionalClassPartsProvider.getAdditionalSupertypes(classDescriptor, supertypes));
if (supertypes.isEmpty()) {
addValidSupertype(supertypes, getDefaultSupertype(classDescriptor));
@@ -19,10 +19,29 @@ object FunctionsFromAny {
val HASH_CODE_METHOD_NAME = Name.identifier("hashCode")
val TO_STRING_METHOD_NAME = Name.identifier("toString")
fun addFunctionFromAnyIfNeeded(
thisDescriptor: ClassDescriptor,
result: MutableCollection<SimpleFunctionDescriptor>,
name: Name,
fromSupertypes: Collection<SimpleFunctionDescriptor>,
) {
if (shouldAddEquals(name, result, fromSupertypes)) {
result.add(createEqualsFunctionDescriptor(thisDescriptor))
}
if (shouldAddHashCode(name, result, fromSupertypes)) {
result.add(createHashCodeFunctionDescriptor(thisDescriptor))
}
if (shouldAddToString(name, result, fromSupertypes)) {
result.add(createToStringFunctionDescriptor(thisDescriptor))
}
}
fun shouldAddEquals(
name: Name,
declaredFunctions: Collection<SimpleFunctionDescriptor>,
fromSupertypes: List<SimpleFunctionDescriptor>
fromSupertypes: Collection<SimpleFunctionDescriptor>
): Boolean {
return name == EQUALS_METHOD_NAME && shouldAddFunctionFromAny(
declaredFunctions,
@@ -36,7 +55,7 @@ object FunctionsFromAny {
fun shouldAddHashCode(
name: Name,
declaredFunctions: Collection<SimpleFunctionDescriptor>,
fromSupertypes: List<SimpleFunctionDescriptor>
fromSupertypes: Collection<SimpleFunctionDescriptor>
): Boolean {
return name == HASH_CODE_METHOD_NAME && shouldAddFunctionFromAny(
declaredFunctions,
@@ -49,7 +68,7 @@ object FunctionsFromAny {
fun shouldAddToString(
name: Name,
declaredFunctions: Collection<SimpleFunctionDescriptor>,
fromSupertypes: List<SimpleFunctionDescriptor>
fromSupertypes: Collection<SimpleFunctionDescriptor>
): Boolean {
return name == TO_STRING_METHOD_NAME && shouldAddFunctionFromAny(
declaredFunctions,
@@ -90,8 +109,8 @@ object FunctionsFromAny {
}
private fun shouldAddFunctionFromAny(
declaredFunctions: Collection <SimpleFunctionDescriptor>,
fromSupertypes: List<SimpleFunctionDescriptor>,
declaredFunctions: Collection<SimpleFunctionDescriptor>,
fromSupertypes: Collection<SimpleFunctionDescriptor>,
checkParameters: (FunctionDescriptor) -> Boolean
): Boolean {
// Add 'equals', 'hashCode', 'toString' iff there is no such declared member AND there is no such final member in supertypes
@@ -47,4 +47,5 @@ interface LazyClassContext {
val wrappedTypeFactory: WrappedTypeFactory
val kotlinTypeChecker: NewKotlinTypeChecker
val samConversionResolver: SamConversionResolver
val additionalClassPartsProvider: AdditionalClassPartsProvider
}
@@ -86,6 +86,8 @@ public class ResolveSession implements KotlinCodeAnalyzer, LazyClassContext {
private PlatformDiagnosticSuppressor platformDiagnosticSuppressor;
private SamConversionResolver samConversionResolver;
private AdditionalClassPartsProvider additionalClassPartsProvider;
private final SyntheticResolveExtension syntheticResolveExtension;
private final NewKotlinTypeChecker kotlinTypeChecker;
@@ -157,6 +159,11 @@ public class ResolveSession implements KotlinCodeAnalyzer, LazyClassContext {
this.samConversionResolver = samConversionResolver;
}
@Inject
public void setAdditionalClassPartsProvider(@NotNull AdditionalClassPartsProvider additionalClassPartsProvider) {
this.additionalClassPartsProvider = additionalClassPartsProvider;
}
// Only calls from injectors expected
@Deprecated
public ResolveSession(
@@ -500,4 +507,10 @@ public class ResolveSession implements KotlinCodeAnalyzer, LazyClassContext {
public SamConversionResolver getSamConversionResolver() {
return samConversionResolver;
}
@NotNull
@Override
public AdditionalClassPartsProvider getAdditionalClassPartsProvider() {
return additionalClassPartsProvider;
}
}
@@ -250,6 +250,9 @@ open class LazyClassMemberScope(
generateDataClassMethods(result, name, location, fromSupertypes)
generateFunctionsFromAnyForInlineClass(result, name, fromSupertypes)
c.syntheticResolveExtension.generateSyntheticMethods(thisDescriptor, name, trace.bindingContext, fromSupertypes, result)
c.additionalClassPartsProvider.generateAdditionalMethods(thisDescriptor, result, name, location, fromSupertypes)
generateFakeOverrides(name, fromSupertypes, result, SimpleFunctionDescriptor::class.java)
}
@@ -259,7 +262,7 @@ open class LazyClassMemberScope(
fromSupertypes: List<SimpleFunctionDescriptor>
) {
if (!thisDescriptor.isInlineClass()) return
addFunctionFromAnyIfNeeded(result, name, fromSupertypes)
FunctionsFromAny.addFunctionFromAnyIfNeeded(thisDescriptor, result, name, fromSupertypes)
}
private fun generateDataClassMethods(
@@ -309,25 +312,7 @@ open class LazyClassMemberScope(
}
if (c.languageVersionSettings.supportsFeature(LanguageFeature.DataClassInheritance)) {
addFunctionFromAnyIfNeeded(result, name, fromSupertypes)
}
}
private fun addFunctionFromAnyIfNeeded(
result: MutableCollection<SimpleFunctionDescriptor>,
name: Name,
fromSupertypes: List<SimpleFunctionDescriptor>
) {
if (FunctionsFromAny.shouldAddEquals(name, result, fromSupertypes)) {
result.add(FunctionsFromAny.createEqualsFunctionDescriptor(thisDescriptor))
}
if (FunctionsFromAny.shouldAddHashCode(name, result, fromSupertypes)) {
result.add(FunctionsFromAny.createHashCodeFunctionDescriptor(thisDescriptor))
}
if (FunctionsFromAny.shouldAddToString(name, result, fromSupertypes)) {
result.add(FunctionsFromAny.createToStringFunctionDescriptor(thisDescriptor))
FunctionsFromAny.addFunctionFromAnyIfNeeded(thisDescriptor, result, name, fromSupertypes)
}
}
@@ -69,7 +69,8 @@ class LocalClassifierAnalyzer(
private val delegationFilter: DelegationFilter,
private val wrappedTypeFactory: WrappedTypeFactory,
private val kotlinTypeChecker: NewKotlinTypeChecker,
private val samConversionResolver: SamConversionResolver
private val samConversionResolver: SamConversionResolver,
private val additionalClassPartsProvider: AdditionalClassPartsProvider,
) {
fun processClassOrObject(
scope: LexicalWritableScope?,
@@ -104,7 +105,8 @@ class LocalClassifierAnalyzer(
delegationFilter,
wrappedTypeFactory,
kotlinTypeChecker,
samConversionResolver
samConversionResolver,
additionalClassPartsProvider,
),
analyzerServices
)
@@ -134,7 +136,8 @@ class LocalClassDescriptorHolder(
val delegationFilter: DelegationFilter,
val wrappedTypeFactory: WrappedTypeFactory,
val kotlinTypeChecker: NewKotlinTypeChecker,
val samConversionResolver: SamConversionResolver
val samConversionResolver: SamConversionResolver,
val additionalClassPartsProvider: AdditionalClassPartsProvider,
) {
// We do not need to synchronize here, because this code is used strictly from one thread
private var classDescriptor: ClassDescriptor? = null
@@ -176,6 +179,8 @@ class LocalClassDescriptorHolder(
override val wrappedTypeFactory: WrappedTypeFactory = this@LocalClassDescriptorHolder.wrappedTypeFactory
override val kotlinTypeChecker: NewKotlinTypeChecker = this@LocalClassDescriptorHolder.kotlinTypeChecker
override val samConversionResolver: SamConversionResolver = this@LocalClassDescriptorHolder.samConversionResolver
override val additionalClassPartsProvider: AdditionalClassPartsProvider =
this@LocalClassDescriptorHolder.additionalClassPartsProvider
},
containingDeclaration,
classOrObject.nameAsSafeName,
@@ -0,0 +1,11 @@
// !LANGUAGE: +JvmRecordSupport
@JvmRecord
class BasicRecord(val x: String)
@JvmRecord
data class BasicDataRecord(val x: String)
@JvmRecord
class BasicRecordWithSuperClass(val x: String) : Record()
@@ -0,0 +1,27 @@
package
@kotlin.jvm.JvmRecord public final data class BasicDataRecord : java.lang.Record {
public constructor BasicDataRecord(/*0*/ x: kotlin.String)
public final val x: kotlin.String
public final operator /*synthesized*/ fun component1(): kotlin.String
public final /*synthesized*/ fun copy(/*0*/ x: kotlin.String = ...): BasicDataRecord
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
}
@kotlin.jvm.JvmRecord public final class BasicRecord : java.lang.Record {
public constructor BasicRecord(/*0*/ x: kotlin.String)
public final val x: kotlin.String
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
}
@kotlin.jvm.JvmRecord public final class BasicRecordWithSuperClass : java.lang.Record {
public constructor BasicRecordWithSuperClass(/*0*/ x: kotlin.String)
public final val x: kotlin.String
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
}
@@ -50,6 +50,11 @@ public class DiagnosticsWithJdk15TestGenerated extends AbstractDiagnosticsWithJd
runTest("compiler/testData/diagnostics/testsWithJava15/jvmRecord/disabledFeature.kt");
}
@TestMetadata("jvmRecordDescriptorStructure.kt")
public void testJvmRecordDescriptorStructure() throws Exception {
runTest("compiler/testData/diagnostics/testsWithJava15/jvmRecord/jvmRecordDescriptorStructure.kt");
}
@TestMetadata("simpleRecords.kt")
public void testSimpleRecords() throws Exception {
runTest("compiler/testData/diagnostics/testsWithJava15/jvmRecord/simpleRecords.kt");
@@ -0,0 +1,44 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.resolve
import org.jetbrains.kotlin.container.DefaultImplementation
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
import org.jetbrains.kotlin.incremental.components.LookupLocation
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.KotlinType
@DefaultImplementation(impl = AdditionalClassPartsProvider.Default::class)
interface AdditionalClassPartsProvider {
fun generateAdditionalMethods(
thisDescriptor: ClassDescriptor,
result: MutableCollection<SimpleFunctionDescriptor>,
name: Name,
location: LookupLocation,
fromSupertypes: Collection<SimpleFunctionDescriptor>
)
fun getAdditionalSupertypes(
thisDescriptor: ClassDescriptor,
existingSupertypes: List<KotlinType>
): List<KotlinType>
object Default : AdditionalClassPartsProvider {
override fun generateAdditionalMethods(
thisDescriptor: ClassDescriptor,
result: MutableCollection<SimpleFunctionDescriptor>,
name: Name,
location: LookupLocation,
fromSupertypes: Collection<SimpleFunctionDescriptor>
) {}
override fun getAdditionalSupertypes(
thisDescriptor: ClassDescriptor,
existingSupertypes: List<KotlinType>
): List<KotlinType> = emptyList()
}
}