diff --git a/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/HasActualMarker.kt b/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/HasActualMarker.kt index 894ea0d7f6f..cdab81a3066 100644 --- a/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/HasActualMarker.kt +++ b/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/HasActualMarker.kt @@ -21,6 +21,7 @@ import com.intellij.ide.util.DefaultPsiElementCellRenderer import com.intellij.psi.PsiElement import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.idea.caches.resolve.findModuleDescriptor +import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor import org.jetbrains.kotlin.idea.core.toDescriptor import org.jetbrains.kotlin.idea.facet.implementingDescriptors @@ -29,6 +30,7 @@ import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils import org.jetbrains.kotlin.resolve.MultiTargetPlatform import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.resolve.getMultiTargetPlatform +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.resolve.multiplatform.ExpectedActualResolver import java.awt.event.MouseEvent @@ -87,8 +89,11 @@ private fun DeclarationDescriptor.actualsForExpected(): Collection { - return unsafeResolveToDescriptor().actualsForExpected().mapNotNullTo(LinkedHashSet()) { - DescriptorToSourceUtils.descriptorToDeclaration(it) as? KtDeclaration - } -} \ No newline at end of file +// null means "any platform" here +internal fun KtDeclaration.actualsForExpected(platform: MultiTargetPlatform? = null): Set = + resolveToDescriptorIfAny(BodyResolveMode.FULL) + ?.actualsForExpected() + ?.filter { platform == null || it.module.getMultiTargetPlatform() == platform } + ?.mapNotNullTo(LinkedHashSet()) { + DescriptorToSourceUtils.descriptorToDeclaration(it) as? KtDeclaration + } ?: emptySet() \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/expectactual/CreateActualFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/expectactual/CreateActualFix.kt index 7c01a7c6d9d..d478fdafc05 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/expectactual/CreateActualFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/expectactual/CreateActualFix.kt @@ -32,6 +32,7 @@ import org.jetbrains.kotlin.diagnostics.DiagnosticFactory import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.idea.core.* import org.jetbrains.kotlin.idea.facet.implementingModules +import org.jetbrains.kotlin.idea.highlighter.markers.actualsForExpected import org.jetbrains.kotlin.idea.project.TargetPlatformDetector import org.jetbrains.kotlin.idea.quickfix.KotlinQuickFixAction import org.jetbrains.kotlin.idea.quickfix.KotlinSingleIntentionActionFactory @@ -40,6 +41,7 @@ import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers 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.hasExpectModifier import org.jetbrains.kotlin.resolve.MultiTargetPlatform import org.jetbrains.kotlin.resolve.getMultiTargetPlatform @@ -94,6 +96,15 @@ sealed class CreateActualFix( private fun getOrCreateImplementationFile(): KtFile? { val declaration = element as? KtNamedDeclaration ?: return null + val parent = declaration.parent + if (parent is KtFile) { + for (otherDeclaration in parent.declarations) { + if (otherDeclaration === declaration) continue + if (!otherDeclaration.hasExpectModifier()) continue + val actualDeclaration = otherDeclaration.actualsForExpected(actualPlatform).singleOrNull() ?: continue + return actualDeclaration.containingKtFile + } + } val name = declaration.name ?: return null val expectedDir = declaration.containingFile.containingDirectory diff --git a/idea/testData/multiModuleQuickFix/functionSameFile/header/header.kt b/idea/testData/multiModuleQuickFix/functionSameFile/header/header.kt new file mode 100644 index 00000000000..e1495a7ea40 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/functionSameFile/header/header.kt @@ -0,0 +1,5 @@ +// "Create actual function for platform JVM" "true" + +expect fun bar() + +expect fun foo(arg: Int): String \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/functionSameFile/header/header.kt.after b/idea/testData/multiModuleQuickFix/functionSameFile/header/header.kt.after new file mode 100644 index 00000000000..34a61d2f679 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/functionSameFile/header/header.kt.after @@ -0,0 +1,5 @@ +// "Create actual function for platform JVM" "true" + +expect fun bar() + +expect fun foo(arg: Int): String \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/functionSameFile/jvm/foo.kt b/idea/testData/multiModuleQuickFix/functionSameFile/jvm/foo.kt new file mode 100644 index 00000000000..7bd63a01cff --- /dev/null +++ b/idea/testData/multiModuleQuickFix/functionSameFile/jvm/foo.kt @@ -0,0 +1,3 @@ +// foo: to be implemented + +actual fun bar() {} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/functionSameFile/jvm/foo.kt.after b/idea/testData/multiModuleQuickFix/functionSameFile/jvm/foo.kt.after new file mode 100644 index 00000000000..1907c67287e --- /dev/null +++ b/idea/testData/multiModuleQuickFix/functionSameFile/jvm/foo.kt.after @@ -0,0 +1,6 @@ +// foo: to be implemented + +actual fun bar() {} +actual fun foo(arg: Int): String { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/sealedSubclass/header/header.kt b/idea/testData/multiModuleQuickFix/sealedSubclass/header/header.kt new file mode 100644 index 00000000000..f2badd93349 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/sealedSubclass/header/header.kt @@ -0,0 +1,7 @@ +// "Create actual class for platform JS" "true" + +expect sealed class Sealed + +expect object Obj : Sealed + +expect class Klass(x: Int) : Sealed diff --git a/idea/testData/multiModuleQuickFix/sealedSubclass/header/header.kt.after b/idea/testData/multiModuleQuickFix/sealedSubclass/header/header.kt.after new file mode 100644 index 00000000000..dd1494bda80 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/sealedSubclass/header/header.kt.after @@ -0,0 +1,7 @@ +// "Create actual class for platform JS" "true" + +expect sealed class Sealed + +expect object Obj : Sealed + +expect class Klass(x: Int) : Sealed diff --git a/idea/testData/multiModuleQuickFix/sealedSubclass/js/Sealed.kt b/idea/testData/multiModuleQuickFix/sealedSubclass/js/Sealed.kt new file mode 100644 index 00000000000..444ae9e8fb4 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/sealedSubclass/js/Sealed.kt @@ -0,0 +1,5 @@ +// Sealed: to be implemented + +actual sealed class Sealed + +actual object Obj : Sealed() \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/sealedSubclass/js/Sealed.kt.after b/idea/testData/multiModuleQuickFix/sealedSubclass/js/Sealed.kt.after new file mode 100644 index 00000000000..705d42755fe --- /dev/null +++ b/idea/testData/multiModuleQuickFix/sealedSubclass/js/Sealed.kt.after @@ -0,0 +1,6 @@ +// Sealed: to be implemented + +actual sealed class Sealed + +actual object Obj : Sealed() +actual class Klass actual constructor(x: Int) : Sealed \ 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 05d8db98e80..846e0eb08d0 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiModuleTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiModuleTest.kt @@ -23,9 +23,9 @@ import org.junit.Test class QuickFixMultiModuleTest : AbstractQuickFixMultiModuleTest() { private fun doMultiPlatformTest( - expectName: String = "header", // todo: change dir name - vararg impls: Pair> = arrayOf("jvm" to TargetPlatformKind.Jvm[JvmTarget.JVM_1_6]), - withTests: Boolean = false + expectName: String = "header", // todo: change dir name + vararg impls: Pair> = arrayOf("jvm" to TargetPlatformKind.Jvm[JvmTarget.JVM_1_6]), + withTests: Boolean = false ) { val commonModule = module(expectName, hasTestRoot = withTests) commonModule.createFacet(TargetPlatformKind.Common, false) @@ -40,7 +40,7 @@ class QuickFixMultiModuleTest : AbstractQuickFixMultiModuleTest() { doQuickFixTest() } - private fun doTestHeaderWithJvmAndJs(expectName: String = "header") { + private fun doTestHeaderWithJvmAndJs() { doMultiPlatformTest(impls = *arrayOf("jvm" to TargetPlatformKind.Jvm[JvmTarget.JVM_1_6], "js" to TargetPlatformKind.JavaScript)) } @@ -109,6 +109,11 @@ class QuickFixMultiModuleTest : AbstractQuickFixMultiModuleTest() { doMultiPlatformTest() } + @Test + fun testFunctionSameFile() { + doMultiPlatformTest() + } + @Test fun testInterface() { doMultiPlatformTest() @@ -169,6 +174,11 @@ class QuickFixMultiModuleTest : AbstractQuickFixMultiModuleTest() { doMultiPlatformTest(impls = *arrayOf("js" to TargetPlatformKind.JavaScript)) } + @Test + fun testSealedSubclass() { + doMultiPlatformTest(impls = *arrayOf("js" to TargetPlatformKind.JavaScript)) + } + @Test fun testSecondaryConstructorAbsence() { doMultiPlatformTest() @@ -265,14 +275,14 @@ class QuickFixMultiModuleTest : AbstractQuickFixMultiModuleTest() { fun testCreateVarInExpectClass() = doMultiPlatformTest() @Test - fun testConvertExpectSealedClassToEnum() = doTestHeaderWithJvmAndJs("header") + fun testConvertExpectSealedClassToEnum() = doTestHeaderWithJvmAndJs() @Test - fun testConvertActualSealedClassToEnum() = doTestHeaderWithJvmAndJs("js") + fun testConvertActualSealedClassToEnum() = doTestHeaderWithJvmAndJs() @Test - fun testConvertExpectEnumToSealedClass() = doTestHeaderWithJvmAndJs("header") + fun testConvertExpectEnumToSealedClass() = doTestHeaderWithJvmAndJs() @Test - fun testConvertActualEnumToSealedClass() = doTestHeaderWithJvmAndJs("js") + fun testConvertActualEnumToSealedClass() = doTestHeaderWithJvmAndJs() }