From f2bd58c0302f0349eec0e8e6d17a4a9e9e2de2d9 Mon Sep 17 00:00:00 2001 From: Dmitry Gridin Date: Tue, 21 May 2019 12:03:57 +0700 Subject: [PATCH] Expand "Create expected..." also to a members if an expect class doesn't exist #KT-31272 Fixed --- .../expectactual/CreateExpectedFix.kt | 34 +++++++++++++------ .../expectClassNoAccessOnMember/jvm/My.kt | 1 + .../expectClassOnMember/header/My.kt | 0 .../expectClassOnMember/header/My.kt.after | 11 ++++++ .../expectClassOnMember/jvm/My.kt | 19 +++++++++++ .../expectClassOnMember/jvm/My.kt.after | 19 +++++++++++ .../QuickFixMultiModuleTestGenerated.java | 5 +++ 7 files changed, 78 insertions(+), 11 deletions(-) create mode 100644 idea/testData/multiModuleQuickFix/expectClassOnMember/header/My.kt create mode 100644 idea/testData/multiModuleQuickFix/expectClassOnMember/header/My.kt.after create mode 100644 idea/testData/multiModuleQuickFix/expectClassOnMember/jvm/My.kt create mode 100644 idea/testData/multiModuleQuickFix/expectClassOnMember/jvm/My.kt.after diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/expectactual/CreateExpectedFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/expectactual/CreateExpectedFix.kt index 55ef005298a..363bd794f00 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/expectactual/CreateExpectedFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/expectactual/CreateExpectedFix.kt @@ -74,27 +74,39 @@ sealed class CreateExpectedFix( // For function we allow it, because overloads are possible if (compatibility.isNotEmpty() && declaration !is KtFunction) return emptyList() - val containingClass = declaration.containingClassOrObject - val expectedContainingClass = containingClass?.liftToExpected() as? KtClassOrObject - if (containingClass != null && expectedContainingClass == null) { - // In this case fix should be invoked on containingClass - return emptyList() - } + val (actualDeclaration, expectedContainingClass) = findFirstActualWithExpectedClass(declaration) + if (compatibility.isNotEmpty() && actualDeclaration !is KtFunction) return emptyList() + // If there is already an expected class, we suggest only for its module, // otherwise we suggest for all relevant expected modules val expectedModules = expectedContainingClass?.module?.let { listOf(it) } - ?: declaration.module?.implementedModules + ?: actualDeclaration.module?.implementedModules ?: return emptyList() - return when (declaration) { - is KtClassOrObject -> expectedModules.map { CreateExpectedClassFix(declaration, expectedContainingClass, it) } - is KtFunction -> expectedModules.map { CreateExpectedFunctionFix(declaration, expectedContainingClass, it) } - is KtProperty, is KtParameter -> expectedModules.map { CreateExpectedPropertyFix(declaration, expectedContainingClass, it) } + return when (actualDeclaration) { + is KtClassOrObject -> expectedModules.map { CreateExpectedClassFix(actualDeclaration, expectedContainingClass, it) } + is KtFunction -> expectedModules.map { CreateExpectedFunctionFix(actualDeclaration, expectedContainingClass, it) } + is KtProperty, is KtParameter -> expectedModules.map { + CreateExpectedPropertyFix( + actualDeclaration, + expectedContainingClass, + it + ) + } else -> emptyList() } } } } +private tailrec fun findFirstActualWithExpectedClass(declaration: KtNamedDeclaration): Pair { + val containingClass = declaration.containingClassOrObject + val expectedContainingClass = containingClass?.liftToExpected() as? KtClassOrObject + return if (containingClass != null && expectedContainingClass == null) + findFirstActualWithExpectedClass(containingClass) + else + declaration to expectedContainingClass +} + class CreateExpectedClassFix( klass: KtClassOrObject, outerExpectedClass: KtClassOrObject?, diff --git a/idea/testData/multiModuleQuickFix/expectClassNoAccessOnMember/jvm/My.kt b/idea/testData/multiModuleQuickFix/expectClassNoAccessOnMember/jvm/My.kt index a1e8f0c8627..a1ee3d6190a 100644 --- a/idea/testData/multiModuleQuickFix/expectClassNoAccessOnMember/jvm/My.kt +++ b/idea/testData/multiModuleQuickFix/expectClassNoAccessOnMember/jvm/My.kt @@ -1,6 +1,7 @@ // "Create expected function in common module testModule_Common" "false" // ACTION: Convert member to extension // ACTION: Convert to block body +// ACTION: Create expected class in common module testModule_Common // ACTION: Move to companion object // ACTION: Remove 'actual' modifier // ACTION: Remove explicit type specification diff --git a/idea/testData/multiModuleQuickFix/expectClassOnMember/header/My.kt b/idea/testData/multiModuleQuickFix/expectClassOnMember/header/My.kt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/testData/multiModuleQuickFix/expectClassOnMember/header/My.kt.after b/idea/testData/multiModuleQuickFix/expectClassOnMember/header/My.kt.after new file mode 100644 index 00000000000..eb527e38c59 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/expectClassOnMember/header/My.kt.after @@ -0,0 +1,11 @@ +expect class My { + fun foo(param: String): Int + fun String.bar(y: Double): Boolean + fun baz() + + constructor(flag: Boolean) + + val isGood: Boolean + var status: Int + +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/expectClassOnMember/jvm/My.kt b/idea/testData/multiModuleQuickFix/expectClassOnMember/jvm/My.kt new file mode 100644 index 00000000000..e7edf7788a8 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/expectClassOnMember/jvm/My.kt @@ -0,0 +1,19 @@ +// "Create expected class in common module testModule_Common" "true" +// DISABLE-ERRORS + +actual class My { + actual fun foo(param: String): Int = 42 + + actual fun String.bar(y: Double): Boolean = true + + actual fun baz() {} + + actual constructor(flag: Boolean) {} + + actual val isGood: Boolean + get() = true + actual var status: Int + get() = 0 + set(value) {} + +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/expectClassOnMember/jvm/My.kt.after b/idea/testData/multiModuleQuickFix/expectClassOnMember/jvm/My.kt.after new file mode 100644 index 00000000000..e7edf7788a8 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/expectClassOnMember/jvm/My.kt.after @@ -0,0 +1,19 @@ +// "Create expected class in common module testModule_Common" "true" +// DISABLE-ERRORS + +actual class My { + actual fun foo(param: String): Int = 42 + + actual fun String.bar(y: Double): Boolean = true + + actual fun baz() {} + + actual constructor(flag: Boolean) {} + + actual val isGood: Boolean + get() = true + actual var status: Int + get() = 0 + set(value) {} + +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiModuleTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiModuleTestGenerated.java index 40315ec663f..18f2fc0580d 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiModuleTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiModuleTestGenerated.java @@ -299,6 +299,11 @@ public class QuickFixMultiModuleTestGenerated extends AbstractQuickFixMultiModul runTest("idea/testData/multiModuleQuickFix/expectClassNoAccessOnMember/"); } + @TestMetadata("expectClassOnMember") + public void testExpectClassOnMember() throws Exception { + runTest("idea/testData/multiModuleQuickFix/expectClassOnMember/"); + } + @TestMetadata("expectClassProperty") public void testExpectClassProperty() throws Exception { runTest("idea/testData/multiModuleQuickFix/expectClassProperty/");