Do not suggest "convert to object" for sealed sub-classes with state
So #KT-24816 Fixed
This commit is contained in:
+40
-2
@@ -20,13 +20,18 @@ import com.intellij.codeInspection.ProblemHighlightType
|
||||
import com.intellij.codeInspection.ProblemsHolder
|
||||
import com.intellij.psi.PsiElementVisitor
|
||||
import org.jetbrains.kotlin.asJava.classes.KtLightClassImpl
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
|
||||
import org.jetbrains.kotlin.idea.core.getModalityFromDescriptor
|
||||
import org.jetbrains.kotlin.idea.quickfix.sealedSubClassToObject.ConvertSealedSubClassToObjectFix
|
||||
import org.jetbrains.kotlin.idea.refactoring.isAbstract
|
||||
import org.jetbrains.kotlin.idea.search.declarationsSearch.HierarchySearchRequest
|
||||
import org.jetbrains.kotlin.idea.search.declarationsSearch.searchInheritors
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.KtClass
|
||||
import org.jetbrains.kotlin.psi.KtProperty
|
||||
import org.jetbrains.kotlin.psi.KtVisitorVoid
|
||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
|
||||
|
||||
class CanSealedSubClassBeObjectInspection : AbstractKotlinInspection() {
|
||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
|
||||
@@ -35,13 +40,16 @@ class CanSealedSubClassBeObjectInspection : AbstractKotlinInspection() {
|
||||
if (!klass.hasModifier(KtTokens.SEALED_KEYWORD)) return
|
||||
if (klass.getModalityFromDescriptor() != KtTokens.SEALED_KEYWORD) return
|
||||
|
||||
klass.getSubclasses()
|
||||
val candidates = klass.getSubclasses()
|
||||
.withEmptyConstructors()
|
||||
.thatAreFinal()
|
||||
.thatHasNoTypeParameters()
|
||||
.thatHasNoInnerClasses()
|
||||
.thatHasNoCompanionObjects()
|
||||
.forEach { reportPossibleObject(it) }
|
||||
.thatHasNoState()
|
||||
if (candidates.isEmpty() || !klass.hasNoState() || !klass.baseClassHasNoState()) return
|
||||
|
||||
candidates.forEach { reportPossibleObject(it) }
|
||||
}
|
||||
|
||||
private fun reportPossibleObject(klass: KtClass) {
|
||||
@@ -56,6 +64,14 @@ class CanSealedSubClassBeObjectInspection : AbstractKotlinInspection() {
|
||||
}
|
||||
}
|
||||
|
||||
private tailrec fun KtClass.baseClassHasNoState(): Boolean {
|
||||
val descriptor = resolveToDescriptorIfAny() ?: return false
|
||||
val superDescriptor = descriptor.getSuperClassNotAny() ?: return true // No super class -- no state
|
||||
val superClass = DescriptorToSourceUtils.descriptorToDeclaration(superDescriptor) as? KtClass ?: return false
|
||||
if (!superClass.hasNoState()) return false
|
||||
return superClass.baseClassHasNoState()
|
||||
}
|
||||
|
||||
private fun KtClass.getSubclasses(): List<KtLightClassImpl> {
|
||||
return HierarchySearchRequest(this, this.useScope, false)
|
||||
.searchInheritors().filterIsInstance<KtLightClassImpl>()
|
||||
@@ -83,6 +99,28 @@ class CanSealedSubClassBeObjectInspection : AbstractKotlinInspection() {
|
||||
return filter { klass -> klass.hasNoInnerClass() }
|
||||
}
|
||||
|
||||
private fun List<KtClass>.thatHasNoState(): List<KtClass> {
|
||||
return filter { it.hasNoState() }
|
||||
}
|
||||
|
||||
private fun KtClass.hasNoState(): Boolean {
|
||||
if (primaryConstructor?.valueParameters?.isNotEmpty() == true) return false
|
||||
val body = getBody()
|
||||
return body == null || run {
|
||||
val properties = body.declarations.filterIsInstance<KtProperty>()
|
||||
properties.none { property ->
|
||||
// Simplified "backing field required"
|
||||
when {
|
||||
property.isAbstract() -> false
|
||||
property.initializer != null -> true
|
||||
property.delegate != null -> false
|
||||
!property.isVar -> property.getter == null
|
||||
else -> property.getter == null || property.setter == null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtClass.hasNoInnerClass(): Boolean {
|
||||
val internalClasses = getBody()
|
||||
?.declarations
|
||||
|
||||
Reference in New Issue
Block a user