From fbada819f2dd2e5b3ac7a2dc89b142d038ca9260 Mon Sep 17 00:00:00 2001 From: Nikita Bobko Date: Thu, 22 Sep 2022 17:43:25 +0200 Subject: [PATCH] ConvertObjectToDataObjectInspection: convert not stateless objects too KTIJ-22087 https://jetbrains.team/p/ij/reviews/95181 https://jetbrains.team/p/ij/reviews/95344 GitOrigin-RevId: 24b28366bc1a800354f63f0a5744c2777078d8bf --- .../CanSealedSubClassBeObjectInspection.kt | 14 ++++++++++---- .../ConvertObjectToDataObjectInspection.kt | 7 +++++-- .../inspections/LocalInspectionTestGenerated.java | 5 +++++ .../sealedNotStatelessSubObject.kt | 4 ++++ .../sealedNotStatelessSubObject.kt.after | 4 ++++ 5 files changed, 28 insertions(+), 6 deletions(-) create mode 100644 plugins/kotlin/idea/tests/testData/inspectionsLocal/convertObjectToDataObject/sealedNotStatelessSubObject.kt create mode 100644 plugins/kotlin/idea/tests/testData/inspectionsLocal/convertObjectToDataObject/sealedNotStatelessSubObject.kt.after diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/CanSealedSubClassBeObjectInspection.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/CanSealedSubClassBeObjectInspection.kt index 380d6cb71e98..ec2909b0f582 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/CanSealedSubClassBeObjectInspection.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/CanSealedSubClassBeObjectInspection.kt @@ -24,6 +24,7 @@ import org.jetbrains.kotlin.lexer.KtModifierKeywordToken import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.platform.jvm.isJvm import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.containingClass import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny import org.jetbrains.kotlin.util.OperatorNameConventions @@ -85,12 +86,17 @@ class CanSealedSubClassBeObjectInspection : AbstractKotlinInspection() { && hasNoStateOrEquals() } - fun KtClassOrObject.isSubclassOfStatelessSealed(): Boolean { - fun KtSuperTypeListEntry.asKtClass(): KtClass? = typeAsUserType?.referenceExpression?.mainReference?.resolve() as? KtClass - return superTypeListEntries.asSequence().mapNotNull { it.asKtClass() }.any { + private fun KtClassOrObject.isSubclassOfStatelessSealed(): Boolean = + superTypeListEntries.asSequence().mapNotNull { it.asKtClass() }.any { it.isSealed() && it.hasNoStateOrEquals() && it.baseClassHasNoStateOrEquals() } - } + + fun KtSuperTypeListEntry.asKtClass(): KtClass? = + when (val resolved = typeAsUserType?.referenceExpression?.mainReference?.resolve()) { + is KtConstructor<*> -> resolved.containingClass() + is KtClass -> resolved + else -> null + } private fun KtClass.withEmptyConstructors(): Boolean = primaryConstructorParameters.isEmpty() && secondaryConstructors.all { it.valueParameters.isEmpty() } diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/ConvertObjectToDataObjectInspection.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/ConvertObjectToDataObjectInspection.kt index 9f04201b9256..be5ef8747bdf 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/ConvertObjectToDataObjectInspection.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/ConvertObjectToDataObjectInspection.kt @@ -20,7 +20,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny import org.jetbrains.kotlin.idea.codeinsight.api.classic.inspections.AbstractKotlinInspection import org.jetbrains.kotlin.idea.core.resolveType -import org.jetbrains.kotlin.idea.inspections.CanSealedSubClassBeObjectInspection.Companion.isSubclassOfStatelessSealed +import org.jetbrains.kotlin.idea.inspections.CanSealedSubClassBeObjectInspection.Companion.asKtClass import org.jetbrains.kotlin.idea.inspections.VirtualFunction.* import org.jetbrains.kotlin.idea.inspections.VirtualFunction.Function import org.jetbrains.kotlin.idea.intentions.conventionNameCalls.* @@ -56,7 +56,7 @@ class ConvertObjectToDataObjectInspection : AbstractKotlinInspection() { val isSerializable = isSerializable(ktObject) val toString = ktObject.findToString() val isSerializableCase = toString == TrivialSuper && isSerializable - val isSealedSubClassCase by lazy { toString == TrivialSuper && ktObject.isSubclassOfStatelessSealed() } + val isSealedSubClassCase by lazy { toString == TrivialSuper && ktObject.isSubclassOfSealed() } val isToStringCase by lazy { toString is Function && isCompatibleToString(ktObject, fqName, toString.function) } if ((isSerializableCase || isSealedSubClassCase || isToStringCase) && isCompatibleHashCode(ktObject) && @@ -79,6 +79,9 @@ class ConvertObjectToDataObjectInspection : AbstractKotlinInspection() { } } +private fun KtObjectDeclaration.isSubclassOfSealed(): Boolean = + superTypeListEntries.asSequence().mapNotNull { it.asKtClass() }.any { it.isSealed() } + private fun isSerializable(ktObject: KtObjectDeclaration): Boolean = ktObject.resolveToDescriptorIfAny() ?.getAllSuperClassifiers() diff --git a/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java b/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java index 17221a30fa7f..f5e43297d6be 100644 --- a/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java +++ b/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java @@ -3932,6 +3932,11 @@ public abstract class LocalInspectionTestGenerated extends AbstractLocalInspecti runTest("testData/inspectionsLocal/convertObjectToDataObject/publicReadResolve.kt"); } + @TestMetadata("sealedNotStatelessSubObject.kt") + public void testSealedNotStatelessSubObject() throws Exception { + runTest("testData/inspectionsLocal/convertObjectToDataObject/sealedNotStatelessSubObject.kt"); + } + @TestMetadata("sealedSubObject.kt") public void testSealedSubObject() throws Exception { runTest("testData/inspectionsLocal/convertObjectToDataObject/sealedSubObject.kt"); diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/convertObjectToDataObject/sealedNotStatelessSubObject.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/convertObjectToDataObject/sealedNotStatelessSubObject.kt new file mode 100644 index 000000000000..6d5f4b7a6393 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/convertObjectToDataObject/sealedNotStatelessSubObject.kt @@ -0,0 +1,4 @@ +// COMPILER_ARGUMENTS: -XXLanguage:+DataObjects + +sealed class Seal(val x: Int) +object Foo : Seal(1) diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/convertObjectToDataObject/sealedNotStatelessSubObject.kt.after b/plugins/kotlin/idea/tests/testData/inspectionsLocal/convertObjectToDataObject/sealedNotStatelessSubObject.kt.after new file mode 100644 index 000000000000..f79251dd4637 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/convertObjectToDataObject/sealedNotStatelessSubObject.kt.after @@ -0,0 +1,4 @@ +// COMPILER_ARGUMENTS: -XXLanguage:+DataObjects + +sealed class Seal(val x: Int) +data object Foo : Seal(1)