Pull Up/Extract Interface: Disable "Make abstract" and assume it to be true for abstract members of an interface
#KT-15355 Fixed
This commit is contained in:
@@ -475,6 +475,7 @@ These artifacts include extensions for the types available in the latter JDKs, s
|
|||||||
- Extract Interface: Disable inline/external/lateinit members
|
- Extract Interface: Disable inline/external/lateinit members
|
||||||
- [`KT-12704`](https://youtrack.jetbrains.com/issue/KT-12704), [`KT-15583`](https://youtrack.jetbrains.com/issue/KT-15583) Override/Implement Members: Support all nullability annotations respected by the Kotlin compiler
|
- [`KT-12704`](https://youtrack.jetbrains.com/issue/KT-12704), [`KT-15583`](https://youtrack.jetbrains.com/issue/KT-15583) Override/Implement Members: Support all nullability annotations respected by the Kotlin compiler
|
||||||
- [`KT-15563`](https://youtrack.jetbrains.com/issue/KT-15563) Override Members: Allow overriding virtual synthetic members (e.g. equals(), hashCode(), toString(), etc.) in data classes
|
- [`KT-15563`](https://youtrack.jetbrains.com/issue/KT-15563) Override Members: Allow overriding virtual synthetic members (e.g. equals(), hashCode(), toString(), etc.) in data classes
|
||||||
|
- [`KT-15355`](https://youtrack.jetbrains.com/issue/KT-15355) Extract Interface: Disable "Make abstract" and assume it to be true for abstract members of an interface
|
||||||
|
|
||||||
#### Intention actions, inspections and quickfixes
|
#### Intention actions, inspections and quickfixes
|
||||||
|
|
||||||
|
|||||||
@@ -41,13 +41,12 @@ import org.jetbrains.kotlin.idea.caches.resolve.getJavaClassDescriptor
|
|||||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
|
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
|
||||||
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideImplementMembersHandler
|
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideImplementMembersHandler
|
||||||
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideMemberChooserObject
|
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideMemberChooserObject
|
||||||
import org.jetbrains.kotlin.idea.refactoring.isInterfaceClass
|
import org.jetbrains.kotlin.idea.refactoring.isAbstract
|
||||||
import org.jetbrains.kotlin.idea.runSynchronouslyWithProgress
|
import org.jetbrains.kotlin.idea.runSynchronouslyWithProgress
|
||||||
import org.jetbrains.kotlin.idea.search.declarationsSearch.HierarchySearchRequest
|
import org.jetbrains.kotlin.idea.search.declarationsSearch.HierarchySearchRequest
|
||||||
import org.jetbrains.kotlin.idea.search.declarationsSearch.searchInheritors
|
import org.jetbrains.kotlin.idea.search.declarationsSearch.searchInheritors
|
||||||
import org.jetbrains.kotlin.idea.util.application.executeCommand
|
import org.jetbrains.kotlin.idea.util.application.executeCommand
|
||||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||||
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.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
||||||
@@ -64,16 +63,6 @@ abstract class ImplementAbstractMemberIntentionBase :
|
|||||||
private val LOG = Logger.getInstance("#${ImplementAbstractMemberIntentionBase::class.java.canonicalName}")
|
private val LOG = Logger.getInstance("#${ImplementAbstractMemberIntentionBase::class.java.canonicalName}")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isAbstract(element: KtNamedDeclaration): Boolean {
|
|
||||||
if (element.hasModifier(KtTokens.ABSTRACT_KEYWORD)) return true
|
|
||||||
if (!(element.containingClassOrObject?.isInterfaceClass() ?: false)) return false
|
|
||||||
return when (element) {
|
|
||||||
is KtProperty -> element.initializer == null && element.delegate == null && element.accessors.isEmpty()
|
|
||||||
is KtNamedFunction -> !element.hasBody()
|
|
||||||
else -> false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected fun findExistingImplementation(
|
protected fun findExistingImplementation(
|
||||||
subClass: ClassDescriptor,
|
subClass: ClassDescriptor,
|
||||||
superMember: CallableMemberDescriptor
|
superMember: CallableMemberDescriptor
|
||||||
@@ -117,7 +106,7 @@ abstract class ImplementAbstractMemberIntentionBase :
|
|||||||
protected abstract fun computeText(element: KtNamedDeclaration): String?
|
protected abstract fun computeText(element: KtNamedDeclaration): String?
|
||||||
|
|
||||||
override fun applicabilityRange(element: KtNamedDeclaration): TextRange? {
|
override fun applicabilityRange(element: KtNamedDeclaration): TextRange? {
|
||||||
if (!isAbstract(element)) return null
|
if (!element.isAbstract()) return null
|
||||||
|
|
||||||
text = computeText(element) ?: return null
|
text = computeText(element) ?: return null
|
||||||
|
|
||||||
|
|||||||
+4
-1
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.idea.refactoring.introduce.extractClass.KotlinExtrac
|
|||||||
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfo
|
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfo
|
||||||
import org.jetbrains.kotlin.idea.refactoring.memberInfo.extractClassMembers
|
import org.jetbrains.kotlin.idea.refactoring.memberInfo.extractClassMembers
|
||||||
import org.jetbrains.kotlin.idea.refactoring.pullUp.getInterfaceContainmentVerifier
|
import org.jetbrains.kotlin.idea.refactoring.pullUp.getInterfaceContainmentVerifier
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.pullUp.isAbstractInInterface
|
||||||
import org.jetbrains.kotlin.idea.refactoring.pullUp.mustBeAbstractInInterface
|
import org.jetbrains.kotlin.idea.refactoring.pullUp.mustBeAbstractInInterface
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
@@ -66,10 +67,12 @@ class KotlinExtractInterfaceDialog(
|
|||||||
override fun isAbstractEnabled(memberInfo: KotlinMemberInfo): Boolean {
|
override fun isAbstractEnabled(memberInfo: KotlinMemberInfo): Boolean {
|
||||||
if (!super.isAbstractEnabled(memberInfo)) return false
|
if (!super.isAbstractEnabled(memberInfo)) return false
|
||||||
val member = memberInfo.member
|
val member = memberInfo.member
|
||||||
|
if (member.isAbstractInInterface(originalClass)) return false
|
||||||
return member is KtNamedFunction || (member is KtProperty && !member.mustBeAbstractInInterface()) || member is KtParameter
|
return member is KtNamedFunction || (member is KtProperty && !member.mustBeAbstractInInterface()) || member is KtParameter
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isAbstractWhenDisabled(member: KotlinMemberInfo) = member.member is KtProperty
|
override fun isAbstractWhenDisabled(member: KotlinMemberInfo) =
|
||||||
|
member.member is KtProperty || member.member.isAbstractInInterface(originalClass)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -716,6 +716,16 @@ fun PsiNamedElement.isInterfaceClass(): Boolean = when (this) {
|
|||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun KtNamedDeclaration.isAbstract(): Boolean {
|
||||||
|
if (hasModifier(KtTokens.ABSTRACT_KEYWORD)) return true
|
||||||
|
if (!(containingClassOrObject?.isInterfaceClass() ?: false)) return false
|
||||||
|
return when (this) {
|
||||||
|
is KtProperty -> initializer == null && delegate == null && accessors.isEmpty()
|
||||||
|
is KtNamedFunction -> !hasBody()
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun <ListType : KtElement> replaceListPsiAndKeepDelimiters(
|
fun <ListType : KtElement> replaceListPsiAndKeepDelimiters(
|
||||||
originalList: ListType,
|
originalList: ListType,
|
||||||
newList: ListType,
|
newList: ListType,
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ class KotlinPullUpDialog(
|
|||||||
if (member.hasModifier(KtTokens.INLINE_KEYWORD) ||
|
if (member.hasModifier(KtTokens.INLINE_KEYWORD) ||
|
||||||
member.hasModifier(KtTokens.EXTERNAL_KEYWORD) ||
|
member.hasModifier(KtTokens.EXTERNAL_KEYWORD) ||
|
||||||
member.hasModifier(KtTokens.LATEINIT_KEYWORD)) return false
|
member.hasModifier(KtTokens.LATEINIT_KEYWORD)) return false
|
||||||
|
if (member.isAbstractInInterface(sourceClass)) return false
|
||||||
if (member.isCompanionMemberOf(sourceClass)) return false
|
if (member.isCompanionMemberOf(sourceClass)) return false
|
||||||
|
|
||||||
if (!superClass.isInterface()) return true
|
if (!superClass.isInterface()) return true
|
||||||
@@ -87,6 +88,7 @@ class KotlinPullUpDialog(
|
|||||||
override fun isAbstractWhenDisabled(memberInfo: KotlinMemberInfo): Boolean {
|
override fun isAbstractWhenDisabled(memberInfo: KotlinMemberInfo): Boolean {
|
||||||
val member = memberInfo.member
|
val member = memberInfo.member
|
||||||
if (member.isCompanionMemberOf(sourceClass)) return false
|
if (member.isCompanionMemberOf(sourceClass)) return false
|
||||||
|
if (member.isAbstractInInterface(sourceClass)) return true
|
||||||
return ((member is KtProperty || member is KtParameter) && superClass !is PsiClass)
|
return ((member is KtProperty || member is KtParameter) && superClass !is PsiClass)
|
||||||
|| (member is KtNamedFunction && superClass is PsiClass)
|
|| (member is KtNamedFunction && superClass is PsiClass)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ import org.jetbrains.kotlin.idea.core.replaced
|
|||||||
import org.jetbrains.kotlin.idea.core.setType
|
import org.jetbrains.kotlin.idea.core.setType
|
||||||
import org.jetbrains.kotlin.idea.refactoring.createJavaField
|
import org.jetbrains.kotlin.idea.refactoring.createJavaField
|
||||||
import org.jetbrains.kotlin.idea.refactoring.dropOverrideKeywordIfNecessary
|
import org.jetbrains.kotlin.idea.refactoring.dropOverrideKeywordIfNecessary
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.isAbstract
|
||||||
import org.jetbrains.kotlin.idea.refactoring.isCompanionMemberOf
|
import org.jetbrains.kotlin.idea.refactoring.isCompanionMemberOf
|
||||||
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KtPsiClassWrapper
|
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KtPsiClassWrapper
|
||||||
import org.jetbrains.kotlin.idea.refactoring.memberInfo.toKtDeclarationWrapperAware
|
import org.jetbrains.kotlin.idea.refactoring.memberInfo.toKtDeclarationWrapperAware
|
||||||
@@ -360,7 +361,7 @@ class KotlinPullUpHelper(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun removeOriginalMemberOrAddOverride(member: KtCallableDeclaration) {
|
private fun removeOriginalMemberOrAddOverride(member: KtCallableDeclaration) {
|
||||||
if (member.hasModifier(KtTokens.ABSTRACT_KEYWORD)) {
|
if (member.isAbstract()) {
|
||||||
member.delete()
|
member.delete()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
|||||||
import org.jetbrains.kotlin.idea.codeInsight.shorten.addToShorteningWaitSet
|
import org.jetbrains.kotlin.idea.codeInsight.shorten.addToShorteningWaitSet
|
||||||
import org.jetbrains.kotlin.idea.core.replaced
|
import org.jetbrains.kotlin.idea.core.replaced
|
||||||
import org.jetbrains.kotlin.idea.core.setType
|
import org.jetbrains.kotlin.idea.core.setType
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.isAbstract
|
||||||
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfo
|
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfo
|
||||||
import org.jetbrains.kotlin.idea.refactoring.memberInfo.lightElementForMemberInfo
|
import org.jetbrains.kotlin.idea.refactoring.memberInfo.lightElementForMemberInfo
|
||||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||||
@@ -40,6 +41,9 @@ import org.jetbrains.kotlin.types.typeUtil.isUnit
|
|||||||
fun KtProperty.mustBeAbstractInInterface() =
|
fun KtProperty.mustBeAbstractInInterface() =
|
||||||
hasInitializer() || hasDelegate() || (!hasInitializer() && !hasDelegate() && accessors.isEmpty())
|
hasInitializer() || hasDelegate() || (!hasInitializer() && !hasDelegate() && accessors.isEmpty())
|
||||||
|
|
||||||
|
fun KtNamedDeclaration.isAbstractInInterface(originalClass: KtClassOrObject) =
|
||||||
|
originalClass is KtClass && originalClass.isInterface() && isAbstract()
|
||||||
|
|
||||||
fun KtNamedDeclaration.canMoveMemberToJavaClass(targetClass: PsiClass): Boolean {
|
fun KtNamedDeclaration.canMoveMemberToJavaClass(targetClass: PsiClass): Boolean {
|
||||||
return when (this) {
|
return when (this) {
|
||||||
is KtProperty, is KtParameter -> {
|
is KtProperty, is KtParameter -> {
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
interface T
|
||||||
|
|
||||||
|
interface <caret>U: T {
|
||||||
|
// INFO: {"checked": "true"}
|
||||||
|
val x: Int
|
||||||
|
// INFO: {"checked": "true"}
|
||||||
|
fun foo(n: Int): Boolean
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
interface T {
|
||||||
|
// INFO: {"checked": "true"}
|
||||||
|
val x: Int
|
||||||
|
|
||||||
|
// INFO: {"checked": "true"}
|
||||||
|
fun foo(n: Int): Boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
interface U: T {
|
||||||
|
}
|
||||||
@@ -34,6 +34,12 @@ public class PullUpTestGenerated extends AbstractPullUpTest {
|
|||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
public static class K2K extends AbstractPullUpTest {
|
public static class K2K extends AbstractPullUpTest {
|
||||||
|
@TestMetadata("abstractFromInterfaceToInterface.kt")
|
||||||
|
public void testAbstractFromInterfaceToInterface() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/k2k/abstractFromInterfaceToInterface.kt");
|
||||||
|
doKotlinTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("accidentalOverrides.kt")
|
@TestMetadata("accidentalOverrides.kt")
|
||||||
public void testAccidentalOverrides() throws Exception {
|
public void testAccidentalOverrides() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/k2k/accidentalOverrides.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/k2k/accidentalOverrides.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user