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-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
@@ -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
}
@@ -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<E : KtExpression>(
}
protected fun getAbstractCallableInfo(mainCallable: CallableInfo, originalExpression: KtExpression): CallableInfo? {
val containingClass = originalExpression.getStrictParentOfType<KtClassOrObject>() 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<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(
@@ -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<E : KtElement>(
originalExpression: E,
@@ -125,11 +126,13 @@ abstract class CreateCallableFromUsageFixBase<E : KtElement>(
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<E : KtElement>(
}
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 } }
@@ -69,6 +69,7 @@ abstract class CreateCallableMemberFromUsageFactory<E : KtElement>(
if (extensionsSupported) {
newCallableQuickFix(originalElementPointer, IntentionActionPriority.LOW, quickFixDataFactory) { element, data ->
if (data.any { it.isAbstract }) return@newCallableQuickFix null
CreateExtensionCallableFromUsageFix(element, data)
}.let { fixes.add(it) }
}
@@ -1,4 +1,4 @@
// "Create abstract function 'foo'" "true"
// "Create abstract function 'A.foo'" "true"
abstract class A {
fun bar(b: Boolean) {}
@@ -1,4 +1,4 @@
// "Create abstract function 'foo'" "true"
// "Create abstract function 'A.foo'" "true"
abstract class A {
fun bar(b: Boolean) {}
@@ -1,4 +1,4 @@
// "Create abstract property 'foo'" "true"
// "Create abstract property 'A.foo'" "true"
abstract class A {
fun bar(b: Boolean) {}
@@ -1,4 +1,4 @@
// "Create abstract property 'foo'" "true"
// "Create abstract property 'A.foo'" "true"
abstract class A {
fun bar(b: Boolean) {}