diff --git a/ChangeLog.md b/ChangeLog.md index dd8695880cc..1d62b57e89c 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -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-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-14019`](https://youtrack.jetbrains.com/issue/KT-14019) Create from Usage: Support generation of abstract members for superclasses #### Refactorings diff --git a/idea/ide-common/src/org/jetbrains/kotlin/idea/util/TypeUtils.kt b/idea/ide-common/src/org/jetbrains/kotlin/idea/util/TypeUtils.kt index 7606cff43a6..36c51137220 100644 --- a/idea/ide-common/src/org/jetbrains/kotlin/idea/util/TypeUtils.kt +++ b/idea/ide-common/src/org/jetbrains/kotlin/idea/util/TypeUtils.kt @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.idea.util import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor import org.jetbrains.kotlin.idea.imports.canBeReferencedViaImport 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.utils.findClassifier 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.addToStdlib.singletonList @@ -141,4 +145,9 @@ fun KotlinType.getResolvableApproximations(scope: LexicalScope?, checkTypeParame it.replace(newArguments) } +} + +fun KotlinType.isAbstract(): Boolean { + val modality = (constructor.declarationDescriptor as? ClassDescriptor)?.modality + return modality == Modality.ABSTRACT || modality == Modality.SEALED } \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableFromCallActionFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableFromCallActionFactory.kt index 3c5532adc44..c3849702d82 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableFromCallActionFactory.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableFromCallActionFactory.kt @@ -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.getExtractionContainers import org.jetbrains.kotlin.idea.refactoring.isInterfaceClass +import org.jetbrains.kotlin.idea.util.isAbstract import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor 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.scopes.receivers.* 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.typeUtil.isSubtypeOf import java.lang.AssertionError @@ -130,17 +133,24 @@ sealed class CreateCallableFromCallActionFactory( } protected fun getAbstractCallableInfo(mainCallable: CallableInfo, originalExpression: KtExpression): CallableInfo? { - val containingClass = originalExpression.getStrictParentOfType() as? KtClass ?: return null - if (!containingClass.isAbstract()) return null + val containingClass: KtClass + val receiverType: KotlinType val receiverTypeInfo = mainCallable.receiverTypeInfo if (receiverTypeInfo != TypeInfo.Empty) { if (receiverTypeInfo !is TypeInfo.ByType) return null - val containingDescriptor = containingClass.resolveToDescriptor() as ClassDescriptor - if (receiverTypeInfo.theType.constructor.declarationDescriptor != containingDescriptor) return null + receiverType = receiverTypeInfo.theType + containingClass = receiverType.constructor.declarationDescriptor?.source?.getPsi() as? KtClass ?: return null + } + else { + containingClass = originalExpression.getStrictParentOfType() 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( diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableFromUsageFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableFromUsageFix.kt index bf46ed891d5..08373c2af5d 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableFromUsageFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableFromUsageFix.kt @@ -25,15 +25,16 @@ import com.intellij.psi.PsiFile import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor import org.jetbrains.kotlin.descriptors.ClassifierDescriptor 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.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.application.executeCommand +import org.jetbrains.kotlin.idea.util.isAbstract import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext -import java.util.HashSet +import java.util.* class CreateCallableFromUsageFix( originalExpression: E, @@ -125,11 +126,13 @@ abstract class CreateCallableFromUsageFixBase( append("Create ") val receiverInfo = callableInfos.first().receiverTypeInfo - if (isExtension) { - append("extension ") - } - else if (receiverInfo !is TypeInfo.Empty) { - append("member ") + if (!callableInfos.any { it.isAbstract }) { + if (isExtension) { + append("extension ") + } + else if (receiverInfo !is TypeInfo.Empty) { + append("member ") + } } renderedCallables.joinTo(this) @@ -188,7 +191,9 @@ abstract class CreateCallableFromUsageFixBase( } 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()) { val containers = receiverTypeCandidates .mapNotNull { candidate -> getDeclarationIfApplicable(project, candidate)?.let { candidate to it } } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableMemberFromUsageFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableMemberFromUsageFactory.kt index f7727403ff9..f1a3e3767d4 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableMemberFromUsageFactory.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableMemberFromUsageFactory.kt @@ -69,6 +69,7 @@ abstract class CreateCallableMemberFromUsageFactory( if (extensionsSupported) { newCallableQuickFix(originalElementPointer, IntentionActionPriority.LOW, quickFixDataFactory) { element, data -> + if (data.any { it.isAbstract }) return@newCallableQuickFix null CreateExtensionCallableFromUsageFix(element, data) }.let { fixes.add(it) } } diff --git a/idea/testData/quickfix/createFromUsage/createFunction/call/abstract/explicitReceiverOfContainingClass.kt b/idea/testData/quickfix/createFromUsage/createFunction/call/abstract/explicitReceiverOfContainingClass.kt index 2d1a864e9f1..3621f8f5d7f 100644 --- a/idea/testData/quickfix/createFromUsage/createFunction/call/abstract/explicitReceiverOfContainingClass.kt +++ b/idea/testData/quickfix/createFromUsage/createFunction/call/abstract/explicitReceiverOfContainingClass.kt @@ -1,4 +1,4 @@ -// "Create abstract function 'foo'" "true" +// "Create abstract function 'A.foo'" "true" abstract class A { fun bar(b: Boolean) {} diff --git a/idea/testData/quickfix/createFromUsage/createFunction/call/abstract/explicitReceiverOfContainingClass.kt.after b/idea/testData/quickfix/createFromUsage/createFunction/call/abstract/explicitReceiverOfContainingClass.kt.after index 6d69552e7f0..99cec0f473c 100644 --- a/idea/testData/quickfix/createFromUsage/createFunction/call/abstract/explicitReceiverOfContainingClass.kt.after +++ b/idea/testData/quickfix/createFromUsage/createFunction/call/abstract/explicitReceiverOfContainingClass.kt.after @@ -1,4 +1,4 @@ -// "Create abstract function 'foo'" "true" +// "Create abstract function 'A.foo'" "true" abstract class A { fun bar(b: Boolean) {} diff --git a/idea/testData/quickfix/createFromUsage/createVariable/property/abstract/explicitReceiverOfContainingClass.kt b/idea/testData/quickfix/createFromUsage/createVariable/property/abstract/explicitReceiverOfContainingClass.kt index bbeb716015d..63c76bd0b1c 100644 --- a/idea/testData/quickfix/createFromUsage/createVariable/property/abstract/explicitReceiverOfContainingClass.kt +++ b/idea/testData/quickfix/createFromUsage/createVariable/property/abstract/explicitReceiverOfContainingClass.kt @@ -1,4 +1,4 @@ -// "Create abstract property 'foo'" "true" +// "Create abstract property 'A.foo'" "true" abstract class A { fun bar(b: Boolean) {} diff --git a/idea/testData/quickfix/createFromUsage/createVariable/property/abstract/explicitReceiverOfContainingClass.kt.after b/idea/testData/quickfix/createFromUsage/createVariable/property/abstract/explicitReceiverOfContainingClass.kt.after index 119a0e94efc..e0d32cdff77 100644 --- a/idea/testData/quickfix/createFromUsage/createVariable/property/abstract/explicitReceiverOfContainingClass.kt.after +++ b/idea/testData/quickfix/createFromUsage/createVariable/property/abstract/explicitReceiverOfContainingClass.kt.after @@ -1,4 +1,4 @@ -// "Create abstract property 'foo'" "true" +// "Create abstract property 'A.foo'" "true" abstract class A { fun bar(b: Boolean) {}