diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/declarations/ConvertMemberToExtensionIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/declarations/ConvertMemberToExtensionIntention.kt index 6daad82b1a8..19e1f6ce51f 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/declarations/ConvertMemberToExtensionIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/declarations/ConvertMemberToExtensionIntention.kt @@ -17,9 +17,12 @@ package org.jetbrains.kotlin.idea.intentions.declarations import com.intellij.codeInsight.intention.LowPriorityAction +import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.ScrollType +import com.intellij.openapi.ui.Messages import com.intellij.openapi.util.TextRange +import com.intellij.psi.PsiComment import com.intellij.psi.PsiMethodCallExpression import com.intellij.psi.PsiReferenceExpression import com.intellij.psi.PsiWhiteSpace @@ -30,61 +33,89 @@ import org.jetbrains.kotlin.asJava.toLightMethods import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor import org.jetbrains.kotlin.idea.core.* +import org.jetbrains.kotlin.idea.highlighter.markers.headerImplementations +import org.jetbrains.kotlin.idea.highlighter.markers.isHeaderOrHeaderClassMember +import org.jetbrains.kotlin.idea.highlighter.markers.liftToHeader import org.jetbrains.kotlin.idea.intentions.SelfTargetingRangeIntention import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.getReturnTypeReference +import org.jetbrains.kotlin.idea.refactoring.withHeaderImplementations import org.jetbrains.kotlin.idea.references.KtReference import org.jetbrains.kotlin.idea.util.ImportInsertHelper +import org.jetbrains.kotlin.idea.util.application.runWriteAction import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.allChildren import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.siblings import org.jetbrains.kotlin.utils.addIfNotNull class ConvertMemberToExtensionIntention : SelfTargetingRangeIntention(KtCallableDeclaration::class.java, "Convert member to extension"), LowPriorityAction { - override fun applicabilityRange(element: KtCallableDeclaration): TextRange? { - val classBody = element.parent as? KtClassBody ?: return null - if (classBody.parent !is KtClass) return null - if (element.receiverTypeReference != null) return null - if (element.hasModifier(KtTokens.OVERRIDE_KEYWORD)) return null + private fun isApplicable(element: KtCallableDeclaration): Boolean { + val classBody = element.parent as? KtClassBody ?: return false + if (classBody.parent !is KtClass) return false + if (element.receiverTypeReference != null) return false + if (element.hasModifier(KtTokens.OVERRIDE_KEYWORD)) return false when (element) { - is KtProperty -> if (element.hasInitializer()) return null - is KtSecondaryConstructor -> return null + is KtProperty -> if (element.hasInitializer()) return false + is KtSecondaryConstructor -> return false } + return true + } + + override fun applicabilityRange(element: KtCallableDeclaration): TextRange? { + if (!element.withHeaderImplementations().all { it is KtCallableDeclaration && isApplicable(it) }) return null return (element.nameIdentifier ?: return null).textRange } + override fun startInWriteAction() = false + //TODO: local class override fun applyTo(element: KtCallableDeclaration, editor: Editor?) { - val (extension, bodyToSelect) = createExtensionCallableAndPrepareBodyToSelect(element) + var allowHeader = true - editor?.apply { - unblockDocument() - - if (bodyToSelect != null) { - val range = bodyToSelect.textRange - moveCaret(range.startOffset, ScrollType.CENTER) - - val parent = bodyToSelect.parent - val lastSibling = - if (parent is KtBlockExpression) - parent.rBrace?.siblings(forward = false, withItself = false)?.first { it !is PsiWhiteSpace } - else - bodyToSelect.siblings(forward = true, withItself = false).lastOrNull() - val endOffset = lastSibling?.endOffset ?: range.endOffset - selectionModel.setSelection(range.startOffset, endOffset) + element.liftToHeader()?.headerImplementations()?.let { + if (it.isEmpty()) { + allowHeader = askIfHeaderIsAllowed(element.containingKtFile) } - else { - moveCaret(extension.textOffset, ScrollType.CENTER) + } + + runWriteAction { + val (extension, bodyToSelect) = createExtensionCallableAndPrepareBodyToSelect(element, allowHeader) + + editor?.apply { + unblockDocument() + + if (bodyToSelect != null) { + val range = bodyToSelect.textRange + moveCaret(range.startOffset, ScrollType.CENTER) + + val parent = bodyToSelect.parent + val lastSibling = + if (parent is KtBlockExpression) + parent.rBrace?.siblings(forward = false, withItself = false)?.first { it !is PsiWhiteSpace } + else + bodyToSelect.siblings(forward = true, withItself = false).lastOrNull() + val endOffset = lastSibling?.endOffset ?: range.endOffset + selectionModel.setSelection(range.startOffset, endOffset) + } + else { + moveCaret(extension.textOffset, ScrollType.CENTER) + } } } } - private fun createExtensionCallableAndPrepareBodyToSelect(element: KtCallableDeclaration): Pair { + private fun processSingleDeclaration( + element: KtCallableDeclaration, + allowHeader: Boolean + ): Pair { val descriptor = element.resolveToDescriptor() val containingClass = descriptor.containingDeclaration as ClassDescriptor + val isEffectiveHeader = allowHeader && element.isHeaderOrHeaderClassMember() + val file = element.containingKtFile val project = file.project val outermostParent = KtPsiUtil.getOutermostParent(element, file, false) @@ -129,6 +160,9 @@ class ConvertMemberToExtensionIntention : SelfTargetingRangeIntention { - if (!extension.hasBody()) { + if (!extension.hasBody() && !isEffectiveHeader) { //TODO: methods in PSI for setBody extension.add(psiFactory.createBlock(bodyText)) selectBody(extension) @@ -157,30 +191,33 @@ class ConvertMemberToExtensionIntention : SelfTargetingRangeIntention { val templateProperty = psiFactory.createDeclaration("var v: Any\nget()=$bodyText\nset(value){\n$bodyText\n}") - val templateGetter = templateProperty.getter!! - val templateSetter = templateProperty.setter!! - var getter = extension.getter - if (getter == null) { - getter = extension.addAfter(templateGetter, extension.typeReference) as KtPropertyAccessor - extension.addBefore(psiFactory.createNewLine(), getter) - selectBody(getter) - } - else if (!getter.hasBody()) { - getter = getter.replace(templateGetter) as KtPropertyAccessor - selectBody(getter) - } + if (!isEffectiveHeader) { + val templateGetter = templateProperty.getter!! + val templateSetter = templateProperty.setter!! - if (extension.isVar) { - var setter = extension.setter - if (setter == null) { - setter = extension.addAfter(templateSetter, getter) as KtPropertyAccessor - extension.addBefore(psiFactory.createNewLine(), setter) - selectBody(setter) + var getter = extension.getter + if (getter == null) { + getter = extension.addAfter(templateGetter, extension.typeReference) as KtPropertyAccessor + extension.addBefore(psiFactory.createNewLine(), getter) + selectBody(getter) } - else if (!setter.hasBody()) { - setter = setter.replace(templateSetter) as KtPropertyAccessor - selectBody(setter) + else if (!getter.hasBody()) { + getter = getter.replace(templateGetter) as KtPropertyAccessor + selectBody(getter) + } + + if (extension.isVar) { + var setter = extension.setter + if (setter == null) { + setter = extension.addAfter(templateSetter, getter) as KtPropertyAccessor + extension.addBefore(psiFactory.createNewLine(), setter) + selectBody(setter) + } + else if (!setter.hasBody()) { + setter = setter.replace(templateSetter) as KtPropertyAccessor + selectBody(setter) + } } } } @@ -210,6 +247,34 @@ class ConvertMemberToExtensionIntention : SelfTargetingRangeIntention { + val headerDeclaration = element.liftToHeader() as? KtCallableDeclaration + if (headerDeclaration != null) { + element.withHeaderImplementations().filterIsInstance().forEach { + if (it != element) { + processSingleDeclaration(it, allowHeader) + } + } + } + + return processSingleDeclaration(element, allowHeader) + } + private fun newTypeParameterList(member: KtCallableDeclaration): KtTypeParameterList? { val classElement = member.parent.parent as KtClass val classParams = classElement.typeParameters diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/kotlinRefactoringUtil.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/kotlinRefactoringUtil.kt index 6424ac48ae0..6e0f0e8cfeb 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/kotlinRefactoringUtil.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/kotlinRefactoringUtil.kt @@ -76,6 +76,8 @@ import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde import org.jetbrains.kotlin.idea.core.* import org.jetbrains.kotlin.idea.core.util.showYesNoCancelDialog +import org.jetbrains.kotlin.idea.highlighter.markers.headerImplementations +import org.jetbrains.kotlin.idea.highlighter.markers.liftToHeader import org.jetbrains.kotlin.idea.intentions.RemoveCurlyBracesFromTemplateIntention import org.jetbrains.kotlin.idea.j2k.IdeaJavaToKotlinServices import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinValVar @@ -962,4 +964,10 @@ fun checkSuperMethodsWithPopup( fun KtNamedDeclaration.isCompanionMemberOf(klass: KtClassOrObject): Boolean { val containingObject = containingClassOrObject as? KtObjectDeclaration ?: return false return containingObject.isCompanion() && containingObject.containingClassOrObject == klass +} + +internal fun KtDeclaration.withHeaderImplementations(): List { + val header = liftToHeader() ?: return listOf(this) + val implementations = header.headerImplementations() ?: emptySet() + return listOf(header) + implementations } \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/memberFunToExtensionByHeader/header/header.kt b/idea/testData/multiModuleQuickFix/memberFunToExtensionByHeader/header/header.kt new file mode 100644 index 00000000000..b1df3649e65 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/memberFunToExtensionByHeader/header/header.kt @@ -0,0 +1,5 @@ +// "Convert member to extension" "true" + +header class Foo { + fun foo(n: Int) +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/memberFunToExtensionByHeader/header/header.kt.after b/idea/testData/multiModuleQuickFix/memberFunToExtensionByHeader/header/header.kt.after new file mode 100644 index 00000000000..ce1261290ba --- /dev/null +++ b/idea/testData/multiModuleQuickFix/memberFunToExtensionByHeader/header/header.kt.after @@ -0,0 +1,6 @@ +// "Convert member to extension" "true" + +header class Foo { +} + +header fun Foo.foo(n: Int) \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/memberFunToExtensionByHeader/js/js.kt b/idea/testData/multiModuleQuickFix/memberFunToExtensionByHeader/js/js.kt new file mode 100644 index 00000000000..706427ed117 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/memberFunToExtensionByHeader/js/js.kt @@ -0,0 +1,5 @@ +impl class Foo { + impl fun foo(n: Int) { + + } +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/memberFunToExtensionByHeader/js/js.kt.after b/idea/testData/multiModuleQuickFix/memberFunToExtensionByHeader/js/js.kt.after new file mode 100644 index 00000000000..2d6f7bb3e80 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/memberFunToExtensionByHeader/js/js.kt.after @@ -0,0 +1,6 @@ +impl class Foo { +} + +impl fun Foo.foo(n: Int) { + +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/memberFunToExtensionByHeader/jvm/jvm.kt b/idea/testData/multiModuleQuickFix/memberFunToExtensionByHeader/jvm/jvm.kt new file mode 100644 index 00000000000..706427ed117 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/memberFunToExtensionByHeader/jvm/jvm.kt @@ -0,0 +1,5 @@ +impl class Foo { + impl fun foo(n: Int) { + + } +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/memberFunToExtensionByHeader/jvm/jvm.kt.after b/idea/testData/multiModuleQuickFix/memberFunToExtensionByHeader/jvm/jvm.kt.after new file mode 100644 index 00000000000..2d6f7bb3e80 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/memberFunToExtensionByHeader/jvm/jvm.kt.after @@ -0,0 +1,6 @@ +impl class Foo { +} + +impl fun Foo.foo(n: Int) { + +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/memberFunToExtensionByImpl/header/header.kt b/idea/testData/multiModuleQuickFix/memberFunToExtensionByImpl/header/header.kt new file mode 100644 index 00000000000..98163e850f4 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/memberFunToExtensionByImpl/header/header.kt @@ -0,0 +1,3 @@ +header class Foo { + fun foo(n: Int) +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/memberFunToExtensionByImpl/header/header.kt.after b/idea/testData/multiModuleQuickFix/memberFunToExtensionByImpl/header/header.kt.after new file mode 100644 index 00000000000..16154e4f3a9 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/memberFunToExtensionByImpl/header/header.kt.after @@ -0,0 +1,4 @@ +header class Foo { +} + +header fun Foo.foo(n: Int) \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/memberFunToExtensionByImpl/js/js.kt b/idea/testData/multiModuleQuickFix/memberFunToExtensionByImpl/js/js.kt new file mode 100644 index 00000000000..d8b544dc5f3 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/memberFunToExtensionByImpl/js/js.kt @@ -0,0 +1,7 @@ +// "Convert member to extension" "true" + +impl class Foo { + impl fun foo(n: Int) { + + } +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/memberFunToExtensionByImpl/js/js.kt.after b/idea/testData/multiModuleQuickFix/memberFunToExtensionByImpl/js/js.kt.after new file mode 100644 index 00000000000..922ecf10e0b --- /dev/null +++ b/idea/testData/multiModuleQuickFix/memberFunToExtensionByImpl/js/js.kt.after @@ -0,0 +1,8 @@ +// "Convert member to extension" "true" + +impl class Foo { +} + +impl fun Foo.foo(n: Int) { + +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/memberFunToExtensionByImpl/jvm/jvm.kt b/idea/testData/multiModuleQuickFix/memberFunToExtensionByImpl/jvm/jvm.kt new file mode 100644 index 00000000000..706427ed117 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/memberFunToExtensionByImpl/jvm/jvm.kt @@ -0,0 +1,5 @@ +impl class Foo { + impl fun foo(n: Int) { + + } +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/memberFunToExtensionByImpl/jvm/jvm.kt.after b/idea/testData/multiModuleQuickFix/memberFunToExtensionByImpl/jvm/jvm.kt.after new file mode 100644 index 00000000000..2d6f7bb3e80 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/memberFunToExtensionByImpl/jvm/jvm.kt.after @@ -0,0 +1,6 @@ +impl class Foo { +} + +impl fun Foo.foo(n: Int) { + +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/memberValToExtensionByHeader/header/header.kt b/idea/testData/multiModuleQuickFix/memberValToExtensionByHeader/header/header.kt new file mode 100644 index 00000000000..47670e30c15 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/memberValToExtensionByHeader/header/header.kt @@ -0,0 +1,5 @@ +// "Convert member to extension" "true" + +header class Foo { + val foo: Int +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/memberValToExtensionByHeader/header/header.kt.after b/idea/testData/multiModuleQuickFix/memberValToExtensionByHeader/header/header.kt.after new file mode 100644 index 00000000000..7f2462d2db0 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/memberValToExtensionByHeader/header/header.kt.after @@ -0,0 +1,6 @@ +// "Convert member to extension" "true" + +header class Foo { +} + +header val Foo.foo: Int \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/memberValToExtensionByHeader/js/js.kt b/idea/testData/multiModuleQuickFix/memberValToExtensionByHeader/js/js.kt new file mode 100644 index 00000000000..79a8afa568c --- /dev/null +++ b/idea/testData/multiModuleQuickFix/memberValToExtensionByHeader/js/js.kt @@ -0,0 +1,3 @@ +impl class Foo { + impl val foo get() = 1 +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/memberValToExtensionByHeader/js/js.kt.after b/idea/testData/multiModuleQuickFix/memberValToExtensionByHeader/js/js.kt.after new file mode 100644 index 00000000000..19c2a85199d --- /dev/null +++ b/idea/testData/multiModuleQuickFix/memberValToExtensionByHeader/js/js.kt.after @@ -0,0 +1,4 @@ +impl class Foo { +} + +impl val Foo.foo get() = 1 \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/memberValToExtensionByHeader/jvm/jvm.kt b/idea/testData/multiModuleQuickFix/memberValToExtensionByHeader/jvm/jvm.kt new file mode 100644 index 00000000000..a82c9eaef6d --- /dev/null +++ b/idea/testData/multiModuleQuickFix/memberValToExtensionByHeader/jvm/jvm.kt @@ -0,0 +1,3 @@ +impl class Foo { + impl val foo get() = 2 +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/memberValToExtensionByHeader/jvm/jvm.kt.after b/idea/testData/multiModuleQuickFix/memberValToExtensionByHeader/jvm/jvm.kt.after new file mode 100644 index 00000000000..8e551c025fa --- /dev/null +++ b/idea/testData/multiModuleQuickFix/memberValToExtensionByHeader/jvm/jvm.kt.after @@ -0,0 +1,4 @@ +impl class Foo { +} + +impl val Foo.foo get() = 2 \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/memberValToExtensionByHeaderWithInapplicableImpl/header/header.kt b/idea/testData/multiModuleQuickFix/memberValToExtensionByHeaderWithInapplicableImpl/header/header.kt new file mode 100644 index 00000000000..7d3c9c16a59 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/memberValToExtensionByHeaderWithInapplicableImpl/header/header.kt @@ -0,0 +1,9 @@ +// "Convert member to extension" "false" +// ACTION: Convert property to function +// ACTION: Introduce backing property +// ACTION: Move to companion object +// ACTION: Move to constructor + +header class Foo { + val foo: Int +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/memberValToExtensionByHeaderWithInapplicableImpl/js/js.kt b/idea/testData/multiModuleQuickFix/memberValToExtensionByHeaderWithInapplicableImpl/js/js.kt new file mode 100644 index 00000000000..3c3da40a108 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/memberValToExtensionByHeaderWithInapplicableImpl/js/js.kt @@ -0,0 +1,3 @@ +impl class Foo { + impl val foo = 1 +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/memberValToExtensionByHeaderWithInapplicableImpl/jvm/jvm.kt b/idea/testData/multiModuleQuickFix/memberValToExtensionByHeaderWithInapplicableImpl/jvm/jvm.kt new file mode 100644 index 00000000000..a82c9eaef6d --- /dev/null +++ b/idea/testData/multiModuleQuickFix/memberValToExtensionByHeaderWithInapplicableImpl/jvm/jvm.kt @@ -0,0 +1,3 @@ +impl class Foo { + impl val foo get() = 2 +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/memberValToExtensionByImpl/header/header.kt b/idea/testData/multiModuleQuickFix/memberValToExtensionByImpl/header/header.kt new file mode 100644 index 00000000000..31265d3f961 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/memberValToExtensionByImpl/header/header.kt @@ -0,0 +1,3 @@ +header class Foo { + val foo: Int +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/memberValToExtensionByImpl/header/header.kt.after b/idea/testData/multiModuleQuickFix/memberValToExtensionByImpl/header/header.kt.after new file mode 100644 index 00000000000..c9025a19882 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/memberValToExtensionByImpl/header/header.kt.after @@ -0,0 +1,4 @@ +header class Foo { +} + +header val Foo.foo: Int \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/memberValToExtensionByImpl/js/js.kt b/idea/testData/multiModuleQuickFix/memberValToExtensionByImpl/js/js.kt new file mode 100644 index 00000000000..4a1704abe1b --- /dev/null +++ b/idea/testData/multiModuleQuickFix/memberValToExtensionByImpl/js/js.kt @@ -0,0 +1,5 @@ +// "Convert member to extension" "true" + +impl class Foo { + impl val foo get() = 1 +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/memberValToExtensionByImpl/js/js.kt.after b/idea/testData/multiModuleQuickFix/memberValToExtensionByImpl/js/js.kt.after new file mode 100644 index 00000000000..a85f629df04 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/memberValToExtensionByImpl/js/js.kt.after @@ -0,0 +1,6 @@ +// "Convert member to extension" "true" + +impl class Foo { +} + +impl val Foo.foo get() = 1 \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/memberValToExtensionByImpl/jvm/jvm.kt b/idea/testData/multiModuleQuickFix/memberValToExtensionByImpl/jvm/jvm.kt new file mode 100644 index 00000000000..a82c9eaef6d --- /dev/null +++ b/idea/testData/multiModuleQuickFix/memberValToExtensionByImpl/jvm/jvm.kt @@ -0,0 +1,3 @@ +impl class Foo { + impl val foo get() = 2 +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/memberValToExtensionByImpl/jvm/jvm.kt.after b/idea/testData/multiModuleQuickFix/memberValToExtensionByImpl/jvm/jvm.kt.after new file mode 100644 index 00000000000..8e551c025fa --- /dev/null +++ b/idea/testData/multiModuleQuickFix/memberValToExtensionByImpl/jvm/jvm.kt.after @@ -0,0 +1,4 @@ +impl class Foo { +} + +impl val Foo.foo get() = 2 \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiModuleTest.kt b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiModuleTest.kt index 182194df024..c767fb1bea5 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiModuleTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiModuleTest.kt @@ -22,25 +22,31 @@ import org.jetbrains.kotlin.idea.stubs.createFacet import org.junit.Test class QuickFixMultiModuleTest : AbstractQuickFixMultiModuleTest() { - - private fun doMultiPlatformTest(headerName: String = "header", - implName: String = "jvm", - implKind: TargetPlatformKind<*> = TargetPlatformKind.Jvm[JvmTarget.JVM_1_6], - withTests: Boolean = false) { + private fun doMultiPlatformTest( + headerName: String = "header", + vararg impls: Pair> = arrayOf("jvm" to TargetPlatformKind.Jvm[JvmTarget.JVM_1_6]), + withTests: Boolean = false + ) { val header = module(headerName, hasTestRoot = withTests) header.createFacet(TargetPlatformKind.Common) - val jvm = module(implName, hasTestRoot = withTests) - jvm.createFacet(implKind) - jvm.enableMultiPlatform() - jvm.addDependency(header) + impls.forEach { (implName, implKind) -> + val implModule = module(implName, hasTestRoot = withTests) + implModule.createFacet(implKind) + implModule.enableMultiPlatform() + implModule.addDependency(header) + } doQuickFixTest() } + private fun doTestHeaderWithJvmAndJs() { + doMultiPlatformTest(impls = *arrayOf("jvm" to TargetPlatformKind.Jvm[JvmTarget.JVM_1_6], "js" to TargetPlatformKind.JavaScript)) + } + @Test fun testAbstract() { - doMultiPlatformTest(implName = "js", implKind = TargetPlatformKind.JavaScript) + doMultiPlatformTest(impls = "js" to TargetPlatformKind.JavaScript) } @Test @@ -50,7 +56,7 @@ class QuickFixMultiModuleTest : AbstractQuickFixMultiModuleTest() { @Test fun testEnum() { - doMultiPlatformTest(implName = "js", implKind = TargetPlatformKind.JavaScript) + doMultiPlatformTest(impls = "js" to TargetPlatformKind.JavaScript) } @Test @@ -95,11 +101,26 @@ class QuickFixMultiModuleTest : AbstractQuickFixMultiModuleTest() { @Test fun testSealed() { - doMultiPlatformTest(implName = "js", implKind = TargetPlatformKind.JavaScript) + doMultiPlatformTest(impls = "js" to TargetPlatformKind.JavaScript) } @Test fun testWithTest() { doMultiPlatformTest(headerName = "common", withTests = true) } + + @Test + fun testMemberFunToExtensionByHeader() = doTestHeaderWithJvmAndJs() + + @Test + fun testMemberFunToExtensionByImpl() = doTestHeaderWithJvmAndJs() + + @Test + fun testMemberValToExtensionByHeader() = doTestHeaderWithJvmAndJs() + + @Test + fun testMemberValToExtensionByHeaderWithInapplicableImpl() = doTestHeaderWithJvmAndJs() + + @Test + fun testMemberValToExtensionByImpl() = doTestHeaderWithJvmAndJs() } \ No newline at end of file