Support of custom 'equals' and 'hashCode' in inline classes

'equals' from any made available for overriding in inline classes
'typed' equals made available for definition in inline classes
'typed' equals definition made compulsory if 'untyped' is overridden
'operator' keyword is allowed in 'typed' equals definition

^KT-24874: Fixed
This commit is contained in:
vladislav.grechko
2022-10-04 20:22:11 +02:00
committed by teamcity
parent 527e8dde27
commit e0c8142106
47 changed files with 684 additions and 53 deletions
@@ -11,9 +11,13 @@ import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.isInlineClass
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.KotlinTypeFactory
import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
import org.jetbrains.kotlin.types.typeUtil.isBoolean
import org.jetbrains.kotlin.types.typeUtil.isNullableAny
import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.utils.sure
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
@@ -88,3 +92,14 @@ fun DeclarationDescriptor.containingPackage(): FqName? {
}
object DeserializedDeclarationsFromSupertypeConflictDataKey : CallableDescriptor.UserDataKey<CallableMemberDescriptor>
fun FunctionDescriptor.isTypedEqualsInInlineClass(): Boolean = name == OperatorNameConventions.EQUALS
&& (returnType?.isBoolean() ?: false) && containingDeclaration.isInlineClass()
&& valueParameters.size == 1 && valueParameters[0].type == (containingDeclaration as? ClassDescriptor)?.defaultType
&& contextReceiverParameters.isEmpty() && extensionReceiverParameter == null
fun FunctionDescriptor.overridesEqualsFromAny(): Boolean = name == OperatorNameConventions.EQUALS
&& (returnType?.isBoolean() ?: false)
&& valueParameters.size == 1 && valueParameters[0].type.isNullableAny()
&& contextReceiverParameters.isEmpty() && extensionReceiverParameter == null
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
import org.jetbrains.kotlin.resolve.descriptorUtil.declaresOrInheritsDefaultValue
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.isInlineClass
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
@@ -198,8 +199,14 @@ object OperatorChecks : AbstractModifierChecks() {
Checks(RANGE_UNTIL, MemberOrExtension, SingleValueParameter, NoDefaultAndVarargsCheck),
Checks(EQUALS, Member) {
fun DeclarationDescriptor.isAny() = this is ClassDescriptor && KotlinBuiltIns.isAny(this)
ensure(containingDeclaration.isAny() || overriddenDescriptors.any { it.containingDeclaration.isAny() }) {
"must override ''equals()'' in Any"
ensure(containingDeclaration.isAny() || overriddenDescriptors.any { it.containingDeclaration.isAny() }
|| (containingDeclaration.isInlineClass() && isTypedEqualsInInlineClass())) {
buildString {
append("must override ''equals()'' in Any")
if (containingDeclaration.isInlineClass()) {
append(" or define ''equals(other: ${containingDeclaration.name}): Boolean''")
}
}
}
},
Checks(COMPARE_TO, MemberOrExtension, ReturnsInt, SingleValueParameter, NoDefaultAndVarargsCheck),