Create from Usage: Support generation of abstract members for superclasses. Skip non-abstract superclasses when generating abstract member

#KT-14019 Fixed
This commit is contained in:
Alexey Sedunov
2016-11-24 18:00:05 +03:00
parent 25cebbab4b
commit 2a594a5bcc
9 changed files with 45 additions and 19 deletions
+1
View File
@@ -336,6 +336,7 @@ These artifacts include extensions for the types available in the latter JDKs, s
- [`KT-14500`](https://youtrack.jetbrains.com/issue/KT-14500) Create from Usage: Suggest functional type based on the call with lambda argument and unresolved invoke() - [`KT-14500`](https://youtrack.jetbrains.com/issue/KT-14500) Create from Usage: Suggest functional type based on the call with lambda argument and unresolved invoke()
- [`KT-14459`](https://youtrack.jetbrains.com/issue/KT-14459) Initialize with Constructor Parameter: Fix IDE freeze on properties in generic class - [`KT-14459`](https://youtrack.jetbrains.com/issue/KT-14459) Initialize with Constructor Parameter: Fix IDE freeze on properties in generic class
- [`KT-14044`](https://youtrack.jetbrains.com/issue/KT-14044) Fix exception on deleting unused declaration in IDEA 2016.3 - [`KT-14044`](https://youtrack.jetbrains.com/issue/KT-14044) Fix exception on deleting unused declaration in IDEA 2016.3
- [`KT-14019`](https://youtrack.jetbrains.com/issue/KT-14019) Create from Usage: Support generation of abstract members for superclasses
#### Refactorings #### Refactorings
@@ -19,6 +19,7 @@
package org.jetbrains.kotlin.idea.util package org.jetbrains.kotlin.idea.util
import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.idea.imports.canBeReferencedViaImport import org.jetbrains.kotlin.idea.imports.canBeReferencedViaImport
import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.incremental.components.NoLookupLocation
@@ -30,7 +31,10 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.scopes.LexicalScope import org.jetbrains.kotlin.resolve.scopes.LexicalScope
import org.jetbrains.kotlin.resolve.scopes.utils.findClassifier import org.jetbrains.kotlin.resolve.scopes.utils.findClassifier
import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.typeUtil.* import org.jetbrains.kotlin.types.typeUtil.builtIns
import org.jetbrains.kotlin.types.typeUtil.immediateSupertypes
import org.jetbrains.kotlin.types.typeUtil.substitute
import org.jetbrains.kotlin.types.typeUtil.supertypes
import org.jetbrains.kotlin.utils.SmartSet import org.jetbrains.kotlin.utils.SmartSet
import org.jetbrains.kotlin.utils.addToStdlib.singletonList import org.jetbrains.kotlin.utils.addToStdlib.singletonList
@@ -141,4 +145,9 @@ fun KotlinType.getResolvableApproximations(scope: LexicalScope?, checkTypeParame
it.replace(newArguments) it.replace(newArguments)
} }
}
fun KotlinType.isAbstract(): Boolean {
val modality = (constructor.declarationDescriptor as? ClassDescriptor)?.modality
return modality == Modality.ABSTRACT || modality == Modality.SEALED
} }
@@ -31,6 +31,7 @@ import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.*
import org.jetbrains.kotlin.idea.refactoring.canRefactor import org.jetbrains.kotlin.idea.refactoring.canRefactor
import org.jetbrains.kotlin.idea.refactoring.getExtractionContainers import org.jetbrains.kotlin.idea.refactoring.getExtractionContainers
import org.jetbrains.kotlin.idea.refactoring.isInterfaceClass import org.jetbrains.kotlin.idea.refactoring.isInterfaceClass
import org.jetbrains.kotlin.idea.util.isAbstract
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
@@ -42,6 +43,8 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.descriptorUtil.classValueType import org.jetbrains.kotlin.resolve.descriptorUtil.classValueType
import org.jetbrains.kotlin.resolve.scopes.receivers.* import org.jetbrains.kotlin.resolve.scopes.receivers.*
import org.jetbrains.kotlin.resolve.source.getPsi import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
import java.lang.AssertionError import java.lang.AssertionError
@@ -130,17 +133,24 @@ sealed class CreateCallableFromCallActionFactory<E : KtExpression>(
} }
protected fun getAbstractCallableInfo(mainCallable: CallableInfo, originalExpression: KtExpression): CallableInfo? { protected fun getAbstractCallableInfo(mainCallable: CallableInfo, originalExpression: KtExpression): CallableInfo? {
val containingClass = originalExpression.getStrictParentOfType<KtClassOrObject>() as? KtClass ?: return null val containingClass: KtClass
if (!containingClass.isAbstract()) return null val receiverType: KotlinType
val receiverTypeInfo = mainCallable.receiverTypeInfo val receiverTypeInfo = mainCallable.receiverTypeInfo
if (receiverTypeInfo != TypeInfo.Empty) { if (receiverTypeInfo != TypeInfo.Empty) {
if (receiverTypeInfo !is TypeInfo.ByType) return null if (receiverTypeInfo !is TypeInfo.ByType) return null
val containingDescriptor = containingClass.resolveToDescriptor() as ClassDescriptor receiverType = receiverTypeInfo.theType
if (receiverTypeInfo.theType.constructor.declarationDescriptor != containingDescriptor) return null containingClass = receiverType.constructor.declarationDescriptor?.source?.getPsi() as? KtClass ?: return null
}
else {
containingClass = originalExpression.getStrictParentOfType<KtClassOrObject>() as? KtClass ?: return null
if (containingClass is KtEnumEntry) return null
receiverType = (containingClass.resolveToDescriptor() as ClassDescriptor).defaultType
} }
return mainCallable.copy(receiverTypeInfo = TypeInfo.Empty, possibleContainers = listOf(containingClass), isAbstract = true) if (!receiverType.isAbstract() && TypeUtils.getAllSupertypes(receiverType).all { !it.isAbstract() }) return null
return mainCallable.copy(receiverTypeInfo = receiverTypeInfo, possibleContainers = listOf(containingClass), isAbstract = true)
} }
protected fun getCallableWithReceiverInsideExtension( protected fun getCallableWithReceiverInsideExtension(
@@ -25,15 +25,16 @@ import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.idea.refactoring.canRefactor
import org.jetbrains.kotlin.idea.refactoring.chooseContainerElementIfNecessary
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.CreateFromUsageFixBase import org.jetbrains.kotlin.idea.quickfix.createFromUsage.CreateFromUsageFixBase
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.* import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.*
import org.jetbrains.kotlin.idea.refactoring.canRefactor
import org.jetbrains.kotlin.idea.refactoring.chooseContainerElementIfNecessary
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.idea.util.application.executeCommand import org.jetbrains.kotlin.idea.util.application.executeCommand
import org.jetbrains.kotlin.idea.util.isAbstract
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
import java.util.HashSet import java.util.*
class CreateCallableFromUsageFix<E : KtElement>( class CreateCallableFromUsageFix<E : KtElement>(
originalExpression: E, originalExpression: E,
@@ -125,11 +126,13 @@ abstract class CreateCallableFromUsageFixBase<E : KtElement>(
append("Create ") append("Create ")
val receiverInfo = callableInfos.first().receiverTypeInfo val receiverInfo = callableInfos.first().receiverTypeInfo
if (isExtension) { if (!callableInfos.any { it.isAbstract }) {
append("extension ") if (isExtension) {
} append("extension ")
else if (receiverInfo !is TypeInfo.Empty) { }
append("member ") else if (receiverInfo !is TypeInfo.Empty) {
append("member ")
}
} }
renderedCallables.joinTo(this) renderedCallables.joinTo(this)
@@ -188,7 +191,9 @@ abstract class CreateCallableFromUsageFixBase<E : KtElement>(
} }
val popupTitle = "Choose target class or interface" val popupTitle = "Choose target class or interface"
val receiverTypeCandidates = callableBuilder.computeTypeCandidates(callableInfo.receiverTypeInfo) val receiverTypeCandidates = callableBuilder.computeTypeCandidates(callableInfo.receiverTypeInfo).let {
if (callableInfo.isAbstract) it.filter { it.theType.isAbstract() } else it
}
if (receiverTypeCandidates.isNotEmpty()) { if (receiverTypeCandidates.isNotEmpty()) {
val containers = receiverTypeCandidates val containers = receiverTypeCandidates
.mapNotNull { candidate -> getDeclarationIfApplicable(project, candidate)?.let { candidate to it } } .mapNotNull { candidate -> getDeclarationIfApplicable(project, candidate)?.let { candidate to it } }
@@ -69,6 +69,7 @@ abstract class CreateCallableMemberFromUsageFactory<E : KtElement>(
if (extensionsSupported) { if (extensionsSupported) {
newCallableQuickFix(originalElementPointer, IntentionActionPriority.LOW, quickFixDataFactory) { element, data -> newCallableQuickFix(originalElementPointer, IntentionActionPriority.LOW, quickFixDataFactory) { element, data ->
if (data.any { it.isAbstract }) return@newCallableQuickFix null
CreateExtensionCallableFromUsageFix(element, data) CreateExtensionCallableFromUsageFix(element, data)
}.let { fixes.add(it) } }.let { fixes.add(it) }
} }
@@ -1,4 +1,4 @@
// "Create abstract function 'foo'" "true" // "Create abstract function 'A.foo'" "true"
abstract class A { abstract class A {
fun bar(b: Boolean) {} fun bar(b: Boolean) {}
@@ -1,4 +1,4 @@
// "Create abstract function 'foo'" "true" // "Create abstract function 'A.foo'" "true"
abstract class A { abstract class A {
fun bar(b: Boolean) {} fun bar(b: Boolean) {}
@@ -1,4 +1,4 @@
// "Create abstract property 'foo'" "true" // "Create abstract property 'A.foo'" "true"
abstract class A { abstract class A {
fun bar(b: Boolean) {} fun bar(b: Boolean) {}
@@ -1,4 +1,4 @@
// "Create abstract property 'foo'" "true" // "Create abstract property 'A.foo'" "true"
abstract class A { abstract class A {
fun bar(b: Boolean) {} fun bar(b: Boolean) {}