Generate equals and hashCode: enable for data classes #KT-17502 Fixed

This commit is contained in:
Toshiaki Kameyama
2018-10-30 16:16:23 +09:00
committed by asedunov
parent b5664755c8
commit 6971ca44e7
3 changed files with 35 additions and 32 deletions
@@ -29,7 +29,6 @@ import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithContent
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.idea.core.CollectingNameValidator
@@ -47,7 +46,6 @@ import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.TargetPlatform
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.utils.addIfNotNull
@@ -70,30 +68,19 @@ class KotlinGenerateEqualsAndHashcodeAction : KotlinGenerateMemberActionBase<Kot
}
class Info(
val needEquals: Boolean,
val needHashCode: Boolean,
val classDescriptor: ClassDescriptor,
val variablesForEquals: List<VariableDescriptor>,
val variablesForHashCode: List<VariableDescriptor>
val needEquals: Boolean,
val needHashCode: Boolean,
val classDescriptor: ClassDescriptor,
val variablesForEquals: List<VariableDescriptor>,
val variablesForHashCode: List<VariableDescriptor>
)
override fun isValidForClass(targetClass: KtClassOrObject): Boolean {
return targetClass is KtClass
&& targetClass !is KtEnumEntry
&& !targetClass.isEnum()
&& !targetClass.isAnnotation()
&& !targetClass.isInterface()
&& (!targetClass.isData() || isValidForDataClass(targetClass))
}
private fun isValidForDataClass(targetClass: KtClass): Boolean {
val constructor = targetClass.primaryConstructor ?: return false
val context = constructor.analyze(BodyResolveMode.PARTIAL)
return constructor.valueParameters.any { parameter ->
parameter.hasValOrVar() && context.get(BindingContext.TYPE, parameter.typeReference)?.let { type ->
KotlinBuiltIns.isArray(type) || KotlinBuiltIns.isPrimitiveArray(type)
} ?: false
}
&& targetClass !is KtEnumEntry
&& !targetClass.isEnum()
&& !targetClass.isAnnotation()
&& !targetClass.isInterface()
}
override fun prepareMembersInfo(klass: KtClassOrObject, project: Project, editor: Editor?): Info? {
@@ -116,7 +103,7 @@ class KotlinGenerateEqualsAndHashcodeAction : KotlinGenerateMemberActionBase<Kot
hashCodeDescriptor.source.getPsi()?.delete()
needEquals = true
needHashCode = true
} catch(e: IncorrectOperationException) {
} catch (e: IncorrectOperationException) {
LOG.error(e)
}
}
@@ -213,8 +200,7 @@ class KotlinGenerateEqualsAndHashcodeAction : KotlinGenerateMemberActionBase<Kot
val useIsCheck = CodeInsightSettings.getInstance().USE_INSTANCEOF_ON_EQUALS_PARAMETER
val isNotInstanceCondition = if (useIsCheck) {
"$paramName !is $typeForCast"
}
else {
} else {
generateClassLiteralsNotEqual(paramName, targetClass)
}
val bodyText = buildString {
@@ -237,11 +223,13 @@ class KotlinGenerateEqualsAndHashcodeAction : KotlinGenerateMemberActionBase<Kot
val isNullable = TypeUtils.isNullableType(it.type)
val isArray = KotlinBuiltIns.isArrayOrPrimitiveArray(it.type)
val canUseArrayContentFunctions = targetClass.canUseArrayContentFunctions()
val propName = (DescriptorToSourceUtilsIde.getAnyDeclaration(project, it) as PsiNameIdentifierOwner).nameIdentifier!!.text
val propName =
(DescriptorToSourceUtilsIde.getAnyDeclaration(project, it) as PsiNameIdentifierOwner).nameIdentifier!!.text
val notEquals = when {
isArray -> {
"!${generateArraysEqualsCall(it, canUseArrayContentFunctions, propName, "$paramName.$propName")}"
} else -> {
}
else -> {
"$propName != $paramName.$propName"
}
}
@@ -332,10 +320,10 @@ class KotlinGenerateEqualsAndHashcodeAction : KotlinGenerateMemberActionBase<Kot
override fun generateMembers(project: Project, editor: Editor?, info: Info): List<KtDeclaration> {
val targetClass = info.classDescriptor.source.getPsi() as KtClass
val prototypes = ArrayList<KtDeclaration>(2)
.apply {
addIfNotNull(generateEquals(project, info, targetClass))
addIfNotNull(generateHashCode(project, info, targetClass))
}
.apply {
addIfNotNull(generateEquals(project, info, targetClass))
addIfNotNull(generateHashCode(project, info, targetClass))
}
val anchor = with(targetClass.declarations) { lastIsInstanceOrNull<KtNamedFunction>() ?: lastOrNull() }
return insertMembersAfter(editor, targetClass, prototypes, anchor)
}
@@ -1,2 +1 @@
// NOT_APPLICABLE
data class A<caret>(val n: Int)
@@ -0,0 +1,16 @@
data class A(val n: Int) {
<caret>override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as A
if (n != other.n) return false
return true
}
override fun hashCode(): Int {
return n
}
}