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:
@@ -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
|
||||||
}
|
}
|
||||||
+15
-5
@@ -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(
|
||||||
|
|||||||
+14
-9
@@ -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 } }
|
||||||
|
|||||||
+1
@@ -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
-1
@@ -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
-1
@@ -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
-1
@@ -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
-1
@@ -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) {}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user