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:
Alexey Sedunov
2017-01-10 21:03:06 +03:00
parent 8c882f0d27
commit 5de4e9fdac
10 changed files with 51 additions and 15 deletions
+1
View File
@@ -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
@@ -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
}
@@ -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");