Create actual: put top-level actual declarations in the same file

When multiple expect declarations are in the same file,
and some of them already have actual declarations for this platform,
then "Create actual fix" put other declarations into the same file
with existing actual declarations

So #KT-17058 Fixed
So #KT-21082 Fixed
This commit is contained in:
Mikhail Glukhikh
2018-03-02 15:37:59 +03:00
parent 3ec2dac10b
commit 90e4413fc9
11 changed files with 83 additions and 13 deletions
@@ -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<DeclarationDe
return emptyList()
}
internal fun KtDeclaration.actualsForExpected(): Set<KtDeclaration> {
return unsafeResolveToDescriptor().actualsForExpected().mapNotNullTo(LinkedHashSet()) {
DescriptorToSourceUtils.descriptorToDeclaration(it) as? KtDeclaration
}
}
// null means "any platform" here
internal fun KtDeclaration.actualsForExpected(platform: MultiTargetPlatform? = null): Set<KtDeclaration> =
resolveToDescriptorIfAny(BodyResolveMode.FULL)
?.actualsForExpected()
?.filter { platform == null || it.module.getMultiTargetPlatform() == platform }
?.mapNotNullTo(LinkedHashSet()) {
DescriptorToSourceUtils.descriptorToDeclaration(it) as? KtDeclaration
} ?: emptySet()
@@ -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<out D : KtNamedDeclaration>(
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
@@ -0,0 +1,5 @@
// "Create actual function for platform JVM" "true"
expect fun bar()
expect fun <caret>foo(arg: Int): String
@@ -0,0 +1,5 @@
// "Create actual function for platform JVM" "true"
expect fun bar()
expect fun foo(arg: Int): String
@@ -0,0 +1,3 @@
// foo: to be implemented
actual fun bar() {}
@@ -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.
}
@@ -0,0 +1,7 @@
// "Create actual class for platform JS" "true"
expect sealed class Sealed
expect object Obj : Sealed
expect class <caret>Klass(x: Int) : Sealed
@@ -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
@@ -0,0 +1,5 @@
// Sealed: to be implemented
actual sealed class Sealed
actual object Obj : Sealed()
@@ -0,0 +1,6 @@
// Sealed: to be implemented
actual sealed class Sealed
actual object Obj : Sealed()
actual class Klass actual constructor(x: Int) : Sealed
@@ -23,9 +23,9 @@ import org.junit.Test
class QuickFixMultiModuleTest : AbstractQuickFixMultiModuleTest() {
private fun doMultiPlatformTest(
expectName: String = "header", // todo: change dir name
vararg impls: Pair<String, TargetPlatformKind<*>> = arrayOf("jvm" to TargetPlatformKind.Jvm[JvmTarget.JVM_1_6]),
withTests: Boolean = false
expectName: String = "header", // todo: change dir name
vararg impls: Pair<String, TargetPlatformKind<*>> = 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()
}