diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/util/expectActualUtil.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/util/expectActualUtil.kt index 9ecee1c550e..19f6e9f71f7 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/util/expectActualUtil.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/util/expectActualUtil.kt @@ -97,7 +97,7 @@ private fun MemberDescriptor.isConstructorInActual(checkConstructor: Boolean) = private fun MemberDescriptor.isEnumEntryInActual() = (DescriptorUtils.isEnumEntry(this) && (containingDeclaration as? MemberDescriptor)?.isActual == true) -private fun DeclarationDescriptor.actualsForExpected(): Collection { +fun DeclarationDescriptor.actualsForExpected(): Collection { if (this is MemberDescriptor) { if (!this.isExpect) return emptyList() diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/MakeOverriddenMemberOpenFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/MakeOverriddenMemberOpenFix.kt index 9ffc47b0083..fda1bc7ff80 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/MakeOverriddenMemberOpenFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/MakeOverriddenMemberOpenFix.kt @@ -13,12 +13,16 @@ import com.intellij.psi.util.CachedValueProvider import com.intellij.psi.util.PsiModificationTracker import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.* +import org.jetbrains.kotlin.descriptors.MemberDescriptor +import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.isOverridable import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny import org.jetbrains.kotlin.idea.core.util.CachedValue import org.jetbrains.kotlin.idea.core.util.getValue import org.jetbrains.kotlin.idea.refactoring.canRefactor +import org.jetbrains.kotlin.idea.util.actualsForExpected +import org.jetbrains.kotlin.idea.util.isExpectDeclaration import org.jetbrains.kotlin.lexer.KtTokens.OPEN_KEYWORD import org.jetbrains.kotlin.psi.KtCallableDeclaration import org.jetbrains.kotlin.psi.KtDeclaration @@ -56,6 +60,17 @@ class MakeOverriddenMemberOpenFix(declaration: KtDeclaration) : KotlinQuickFixAc ) { return QUICKFIX_UNAVAILABLE } + + overriddenDescriptor.takeIf { overriddenMember.isExpectDeclaration() }?.actualsForExpected()?.forEach { + if (it is MemberDescriptor && it.modality < Modality.OPEN) { + val member = DescriptorToSourceUtils.descriptorToDeclaration(it) + if (member == null || !member.canRefactor() || member !is KtCallableDeclaration) { + return QUICKFIX_UNAVAILABLE + } + overriddenNonOverridableMembers.add(member.createSmartPointer()) + } + } + val containingDeclarationName = overriddenDescriptor.containingDeclaration.name.asString() overriddenNonOverridableMembers.add(overriddenMember.createSmartPointer()) containingDeclarationsNames.add(containingDeclarationName) @@ -69,7 +84,7 @@ class MakeOverriddenMemberOpenFix(declaration: KtDeclaration) : KotlinQuickFixAc override fun getText(): String { val element = element ?: return "" - if (overriddenNonOverridableMembers.size == 1) { + if (containingDeclarationsNames.size == 1) { val name = containingDeclarationsNames[0] + "." + element.name return "Make $name $OPEN_KEYWORD" } diff --git a/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/actual/common/common.kt b/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/actual/common/common.kt new file mode 100644 index 00000000000..1759c01c988 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/actual/common/common.kt @@ -0,0 +1,3 @@ +expect open class OClass() { + val overrideMe: String +} diff --git a/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/actual/common/common.kt.after b/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/actual/common/common.kt.after new file mode 100644 index 00000000000..1759c01c988 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/actual/common/common.kt.after @@ -0,0 +1,3 @@ +expect open class OClass() { + val overrideMe: String +} diff --git a/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/actual/jvm/jvm.kt b/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/actual/jvm/jvm.kt new file mode 100644 index 00000000000..639dfe549b3 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/actual/jvm/jvm.kt @@ -0,0 +1,9 @@ +// "Make OClass.overrideMe open" "true" + +actual open class OClass actual constructor() { + actual val overrideMe: String = "" +} + +class Another: OClass() { + override val overrideMe = "" +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/actual/jvm/jvm.kt.after b/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/actual/jvm/jvm.kt.after new file mode 100644 index 00000000000..4113c8a895f --- /dev/null +++ b/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/actual/jvm/jvm.kt.after @@ -0,0 +1,9 @@ +// "Make OClass.overrideMe open" "true" + +actual open class OClass actual constructor() { + actual open val overrideMe: String = "" +} + +class Another: OClass() { + override val overrideMe = "" +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/expect/common/common.kt b/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/expect/common/common.kt new file mode 100644 index 00000000000..155c8a23f70 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/expect/common/common.kt @@ -0,0 +1,9 @@ +// "Make OClass.overrideMe open" "true" + +expect open class OClass() { + val overrideMe: String +} + +class Another: OClass() { + override val overrideMe = "" +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/expect/common/common.kt.after b/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/expect/common/common.kt.after new file mode 100644 index 00000000000..69f7075c2fb --- /dev/null +++ b/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/expect/common/common.kt.after @@ -0,0 +1,9 @@ +// "Make OClass.overrideMe open" "true" + +expect open class OClass() { + open val overrideMe: String +} + +class Another: OClass() { + override val overrideMe = "" +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/expect/jvm/jvm.kt b/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/expect/jvm/jvm.kt new file mode 100644 index 00000000000..ff3d7e2783b --- /dev/null +++ b/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/expect/jvm/jvm.kt @@ -0,0 +1,3 @@ +actual open class OClass actual constructor() { + actual val overrideMe: String = "" +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/expect/jvm/jvm.kt.after b/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/expect/jvm/jvm.kt.after new file mode 100644 index 00000000000..84d6014b2a8 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/expect/jvm/jvm.kt.after @@ -0,0 +1,3 @@ +actual open class OClass actual constructor() { + actual open val overrideMe: String = "" +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/hasAbstract/common/common.kt b/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/hasAbstract/common/common.kt new file mode 100644 index 00000000000..155c8a23f70 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/hasAbstract/common/common.kt @@ -0,0 +1,9 @@ +// "Make OClass.overrideMe open" "true" + +expect open class OClass() { + val overrideMe: String +} + +class Another: OClass() { + override val overrideMe = "" +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/hasAbstract/common/common.kt.after b/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/hasAbstract/common/common.kt.after new file mode 100644 index 00000000000..69f7075c2fb --- /dev/null +++ b/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/hasAbstract/common/common.kt.after @@ -0,0 +1,9 @@ +// "Make OClass.overrideMe open" "true" + +expect open class OClass() { + open val overrideMe: String +} + +class Another: OClass() { + override val overrideMe = "" +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/hasAbstract/jvm/jvm.kt b/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/hasAbstract/jvm/jvm.kt new file mode 100644 index 00000000000..e20f3c9f23d --- /dev/null +++ b/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/hasAbstract/jvm/jvm.kt @@ -0,0 +1,3 @@ +actual abstract class OClass actual constructor() { + actual abstract val overrideMe: String +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/hasAbstract/jvm/jvm.kt.after b/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/hasAbstract/jvm/jvm.kt.after new file mode 100644 index 00000000000..e20f3c9f23d --- /dev/null +++ b/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/hasAbstract/jvm/jvm.kt.after @@ -0,0 +1,3 @@ +actual abstract class OClass actual constructor() { + actual abstract val overrideMe: String +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/hasOpen/common/common.kt b/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/hasOpen/common/common.kt new file mode 100644 index 00000000000..155c8a23f70 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/hasOpen/common/common.kt @@ -0,0 +1,9 @@ +// "Make OClass.overrideMe open" "true" + +expect open class OClass() { + val overrideMe: String +} + +class Another: OClass() { + override val overrideMe = "" +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/hasOpen/common/common.kt.after b/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/hasOpen/common/common.kt.after new file mode 100644 index 00000000000..69f7075c2fb --- /dev/null +++ b/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/hasOpen/common/common.kt.after @@ -0,0 +1,9 @@ +// "Make OClass.overrideMe open" "true" + +expect open class OClass() { + open val overrideMe: String +} + +class Another: OClass() { + override val overrideMe = "" +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/hasOpen/jvm/jvm.kt b/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/hasOpen/jvm/jvm.kt new file mode 100644 index 00000000000..84d6014b2a8 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/hasOpen/jvm/jvm.kt @@ -0,0 +1,3 @@ +actual open class OClass actual constructor() { + actual open val overrideMe: String = "" +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/hasOpen/jvm/jvm.kt.after b/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/hasOpen/jvm/jvm.kt.after new file mode 100644 index 00000000000..84d6014b2a8 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/hasOpen/jvm/jvm.kt.after @@ -0,0 +1,3 @@ +actual open class OClass actual constructor() { + actual open val overrideMe: String = "" +} \ 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 55f8f5c6953..c0167d9ac41 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiModuleTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiModuleTestGenerated.java @@ -558,6 +558,39 @@ public class QuickFixMultiModuleTestGenerated extends AbstractQuickFixMultiModul } } + @TestMetadata("idea/testData/multiModuleQuickFix/makeOverridenMemberOpen") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class MakeOverridenMemberOpen extends AbstractQuickFixMultiModuleTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath); + } + + @TestMetadata("actual") + public void testActual() throws Exception { + runTest("idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/actual/"); + } + + public void testAllFilesPresentInMakeOverridenMemberOpen() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/multiModuleQuickFix/makeOverridenMemberOpen"), Pattern.compile("^([^\\.]+)$"), TargetBackend.ANY, true); + } + + @TestMetadata("expect") + public void testExpect() throws Exception { + runTest("idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/expect/"); + } + + @TestMetadata("hasAbstract") + public void testHasAbstract() throws Exception { + runTest("idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/hasAbstract/"); + } + + @TestMetadata("hasOpen") + public void testHasOpen() throws Exception { + runTest("idea/testData/multiModuleQuickFix/makeOverridenMemberOpen/hasOpen/"); + } + } + @TestMetadata("idea/testData/multiModuleQuickFix/other") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)