Improve support of custom equals in inline classes
- Ensure that typed equals parameter's type is a star projection of corresponding inline class - Make possible to declare typed equals that returns 'Nothing' - Forbid type parameters in typed equals operator declaration ^KT-54909 fixed ^KT-54910 fixed
This commit is contained in:
@@ -10,14 +10,11 @@ import org.jetbrains.kotlin.incremental.components.LookupLocation
|
||||
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.classId
|
||||
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.types.typeUtil.*
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.kotlin.utils.sure
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
@@ -94,10 +91,16 @@ fun DeclarationDescriptor.containingPackage(): FqName? {
|
||||
|
||||
object DeserializedDeclarationsFromSupertypeConflictDataKey : CallableDescriptor.UserDataKey<CallableMemberDescriptor>
|
||||
|
||||
fun FunctionDescriptor.isTypedEqualsInInlineClass() = name == OperatorNameConventions.EQUALS
|
||||
&& (returnType?.isBoolean() ?: false) && containingDeclaration.isInlineClass()
|
||||
&& valueParameters.size == 1 && valueParameters[0].type.constructor.declarationDescriptor.classId == (containingDeclaration as? ClassDescriptor)?.classId
|
||||
&& contextReceiverParameters.isEmpty() && extensionReceiverParameter == null
|
||||
fun FunctionDescriptor.isTypedEqualsInInlineClass(): Boolean {
|
||||
val inlineClassStarProjection =
|
||||
(containingDeclaration as? ClassDescriptor)?.takeIf { it.isInlineClass() }?.defaultType?.replaceArgumentsWithStarProjections()
|
||||
?: return false
|
||||
val returnType = returnType ?: return false
|
||||
return name == OperatorNameConventions.EQUALS
|
||||
&& (returnType.isBoolean() || returnType.isNothing())
|
||||
&& valueParameters.size == 1 && valueParameters[0].type == inlineClassStarProjection
|
||||
&& contextReceiverParameters.isEmpty() && extensionReceiverParameter == null
|
||||
}
|
||||
|
||||
|
||||
fun FunctionDescriptor.overridesEqualsFromAny(): Boolean = name == OperatorNameConventions.EQUALS
|
||||
|
||||
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.builtins.ReflectionTypes
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.declaresOrInheritsDefaultValue
|
||||
@@ -29,6 +30,7 @@ import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
||||
import org.jetbrains.kotlin.types.typeUtil.replaceArgumentsWithStarProjections
|
||||
import org.jetbrains.kotlin.util.MemberKindCheck.Member
|
||||
import org.jetbrains.kotlin.util.MemberKindCheck.MemberOrExtension
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions.ASSIGNMENT_OPERATIONS
|
||||
@@ -198,12 +200,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() }
|
||||
|| (containingDeclaration.isInlineClass() && isTypedEqualsInInlineClass())) {
|
||||
ensure(containingDeclaration.isAny() || overriddenDescriptors.any { it.containingDeclaration.isAny() } || isTypedEqualsInInlineClass()) {
|
||||
buildString {
|
||||
append("must override ''equals()'' in Any")
|
||||
if (containingDeclaration.isInlineClass()) {
|
||||
append(" or define ''equals(other: ${containingDeclaration.name}): Boolean''")
|
||||
val expectedParameterTypeRendered = DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(
|
||||
(containingDeclaration as ClassDescriptor).defaultType.replaceArgumentsWithStarProjections()
|
||||
)
|
||||
append(" or define ''equals(other: $expectedParameterTypeRendered): Boolean''")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user