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
|
||||
- [`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-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
|
||||
|
||||
|
||||
@@ -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.core.overrideImplement.OverrideImplementMembersHandler
|
||||
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.search.declarationsSearch.HierarchySearchRequest
|
||||
import org.jetbrains.kotlin.idea.search.declarationsSearch.searchInheritors
|
||||
import org.jetbrains.kotlin.idea.util.application.executeCommand
|
||||
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.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
||||
@@ -64,16 +63,6 @@ abstract class ImplementAbstractMemberIntentionBase :
|
||||
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(
|
||||
subClass: ClassDescriptor,
|
||||
superMember: CallableMemberDescriptor
|
||||
@@ -117,7 +106,7 @@ abstract class ImplementAbstractMemberIntentionBase :
|
||||
protected abstract fun computeText(element: KtNamedDeclaration): String?
|
||||
|
||||
override fun applicabilityRange(element: KtNamedDeclaration): TextRange? {
|
||||
if (!isAbstract(element)) return null
|
||||
if (!element.isAbstract()) 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.extractClassMembers
|
||||
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.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
@@ -66,10 +67,12 @@ class KotlinExtractInterfaceDialog(
|
||||
override fun isAbstractEnabled(memberInfo: KotlinMemberInfo): Boolean {
|
||||
if (!super.isAbstractEnabled(memberInfo)) return false
|
||||
val member = memberInfo.member
|
||||
if (member.isAbstractInInterface(originalClass)) return false
|
||||
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
|
||||
}
|
||||
|
||||
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(
|
||||
originalList: ListType,
|
||||
newList: ListType,
|
||||
|
||||
@@ -77,6 +77,7 @@ class KotlinPullUpDialog(
|
||||
if (member.hasModifier(KtTokens.INLINE_KEYWORD) ||
|
||||
member.hasModifier(KtTokens.EXTERNAL_KEYWORD) ||
|
||||
member.hasModifier(KtTokens.LATEINIT_KEYWORD)) return false
|
||||
if (member.isAbstractInInterface(sourceClass)) return false
|
||||
if (member.isCompanionMemberOf(sourceClass)) return false
|
||||
|
||||
if (!superClass.isInterface()) return true
|
||||
@@ -87,6 +88,7 @@ class KotlinPullUpDialog(
|
||||
override fun isAbstractWhenDisabled(memberInfo: KotlinMemberInfo): Boolean {
|
||||
val member = memberInfo.member
|
||||
if (member.isCompanionMemberOf(sourceClass)) return false
|
||||
if (member.isAbstractInInterface(sourceClass)) return true
|
||||
return ((member is KtProperty || member is KtParameter) && 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.refactoring.createJavaField
|
||||
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.memberInfo.KtPsiClassWrapper
|
||||
import org.jetbrains.kotlin.idea.refactoring.memberInfo.toKtDeclarationWrapperAware
|
||||
@@ -360,7 +361,7 @@ class KotlinPullUpHelper(
|
||||
}
|
||||
|
||||
private fun removeOriginalMemberOrAddOverride(member: KtCallableDeclaration) {
|
||||
if (member.hasModifier(KtTokens.ABSTRACT_KEYWORD)) {
|
||||
if (member.isAbstract()) {
|
||||
member.delete()
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.idea.codeInsight.shorten.addToShorteningWaitSet
|
||||
import org.jetbrains.kotlin.idea.core.replaced
|
||||
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.lightElementForMemberInfo
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||
@@ -40,6 +41,9 @@ import org.jetbrains.kotlin.types.typeUtil.isUnit
|
||||
fun KtProperty.mustBeAbstractInInterface() =
|
||||
hasInitializer() || hasDelegate() || (!hasInitializer() && !hasDelegate() && accessors.isEmpty())
|
||||
|
||||
fun KtNamedDeclaration.isAbstractInInterface(originalClass: KtClassOrObject) =
|
||||
originalClass is KtClass && originalClass.isInterface() && isAbstract()
|
||||
|
||||
fun KtNamedDeclaration.canMoveMemberToJavaClass(targetClass: PsiClass): Boolean {
|
||||
return when (this) {
|
||||
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")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
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")
|
||||
public void testAccidentalOverrides() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/k2k/accidentalOverrides.kt");
|
||||
|
||||
Reference in New Issue
Block a user