diff --git a/plugins/kotlin/code-insight/fixes-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/fixes/LetImplementInterfaceFixFactories.kt b/plugins/kotlin/code-insight/fixes-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/fixes/LetImplementInterfaceFixFactories.kt index 966468726952..94a0a668978f 100644 --- a/plugins/kotlin/code-insight/fixes-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/fixes/LetImplementInterfaceFixFactories.kt +++ b/plugins/kotlin/code-insight/fixes-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/fixes/LetImplementInterfaceFixFactories.kt @@ -2,6 +2,7 @@ package org.jetbrains.kotlin.idea.k2.codeinsight.fixes import com.intellij.codeInsight.intention.LowPriorityAction +import com.intellij.openapi.actionSystem.ex.ActionUtil import com.intellij.openapi.editor.Editor import com.intellij.openapi.fileEditor.FileEditorManager import com.intellij.openapi.project.Project @@ -22,10 +23,12 @@ import org.jetbrains.kotlin.idea.codeinsight.api.classic.quickfixes.KotlinQuickF import org.jetbrains.kotlin.idea.codeinsight.utils.containsStarProjections import org.jetbrains.kotlin.idea.codeinsight.utils.isInterface import org.jetbrains.kotlin.idea.core.overrideImplement.KtImplementMembersHandler +import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtPsiFactory import org.jetbrains.kotlin.psi.psiUtil.isObjectLiteral +import org.jetbrains.kotlin.psi.psiUtil.visibilityModifierType import org.jetbrains.kotlin.types.Variance internal object LetImplementInterfaceFixFactories { @@ -144,7 +147,25 @@ internal object LetImplementInterfaceFixFactories { } val newElement = point.element ?: return + val implementMembersHandler = KtImplementMembersHandler() + + val membersToAddOverride = ActionUtil.underModalProgress(project, KotlinBundle.message("progress.title.collect.members.to.generate")) { + implementMembersHandler.collectMembersToAddOverride(newElement) + } + + if (membersToAddOverride.isNotEmpty()) { + application.runWriteAction { + membersToAddOverride.forEach { + it.addModifier(KtTokens.OVERRIDE_KEYWORD) + val visibility = element.visibilityModifierType() + if (visibility != null && visibility != KtTokens.PUBLIC_KEYWORD) { + element.removeModifier(visibility) + } + } + } + } + if (implementMembersHandler.collectMembersToGenerateUnderProgress(newElement).isEmpty()) return if (editor != null) { diff --git a/plugins/kotlin/code-insight/fixes-k2/tests/test/org/jetbrains/kotlin/idea/k2/codeinsight/fixes/HighLevelQuickFixTestGenerated.java b/plugins/kotlin/code-insight/fixes-k2/tests/test/org/jetbrains/kotlin/idea/k2/codeinsight/fixes/HighLevelQuickFixTestGenerated.java index a20871ca221e..7b58a9654a66 100644 --- a/plugins/kotlin/code-insight/fixes-k2/tests/test/org/jetbrains/kotlin/idea/k2/codeinsight/fixes/HighLevelQuickFixTestGenerated.java +++ b/plugins/kotlin/code-insight/fixes-k2/tests/test/org/jetbrains/kotlin/idea/k2/codeinsight/fixes/HighLevelQuickFixTestGenerated.java @@ -4453,6 +4453,11 @@ public abstract class HighLevelQuickFixTestGenerated extends AbstractHighLevelQu KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); } + @TestMetadata("hiddenVirtualMethod.kt") + public void testHiddenVirtualMethod() throws Exception { + runTest("../../../idea/tests/testData/quickfix/typeMismatch/letImplementInterface/hiddenVirtualMethod.kt"); + } + @TestMetadata("kt25928.kt") public void testKt25928() throws Exception { runTest("../../../idea/tests/testData/quickfix/typeMismatch/letImplementInterface/kt25928.kt"); diff --git a/plugins/kotlin/code-insight/override-implement-k2/src/org/jetbrains/kotlin/idea/core/overrideImplement/KtImplementMembersHandler.kt b/plugins/kotlin/code-insight/override-implement-k2/src/org/jetbrains/kotlin/idea/core/overrideImplement/KtImplementMembersHandler.kt index d92897041374..1429fb14a481 100644 --- a/plugins/kotlin/code-insight/override-implement-k2/src/org/jetbrains/kotlin/idea/core/overrideImplement/KtImplementMembersHandler.kt +++ b/plugins/kotlin/code-insight/override-implement-k2/src/org/jetbrains/kotlin/idea/core/overrideImplement/KtImplementMembersHandler.kt @@ -7,6 +7,7 @@ import com.intellij.openapi.editor.Editor import com.intellij.openapi.project.Project import com.intellij.openapi.util.NlsSafe import com.intellij.psi.PsiFile +import com.intellij.util.containers.addIfNotNull import org.jetbrains.annotations.ApiStatus import org.jetbrains.kotlin.analysis.api.KaExperimentalApi import org.jetbrains.kotlin.analysis.api.KaSession @@ -16,11 +17,13 @@ import org.jetbrains.kotlin.analysis.api.symbols.KaCallableSymbol import org.jetbrains.kotlin.analysis.api.symbols.KaClassKind import org.jetbrains.kotlin.analysis.api.symbols.KaClassSymbol import org.jetbrains.kotlin.analysis.api.symbols.KaSymbolModality +import org.jetbrains.kotlin.fir.extensions.FirAnalysisHandlerExtension.Companion.analyze import org.jetbrains.kotlin.idea.KtIconProvider.getIcon import org.jetbrains.kotlin.idea.codeinsight.api.applicators.fixes.KotlinQuickFixFactory import org.jetbrains.kotlin.idea.core.overrideImplement.KtImplementMembersHandler.Companion.getUnimplementedMembers import org.jetbrains.kotlin.idea.core.util.KotlinIdeaCoreBundle import org.jetbrains.kotlin.idea.search.ExpectActualSupport +import org.jetbrains.kotlin.psi.KtCallableDeclaration import org.jetbrains.kotlin.psi.KtClass import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtEnumEntry @@ -40,6 +43,18 @@ open class KtImplementMembersHandler : KtGenerateMembersHandler(true) { } } + @OptIn(KaExperimentalApi::class) + fun collectMembersToAddOverride(classOrObject: KtClassOrObject): List { + analyze(classOrObject) { + val classSymbol = classOrObject.classSymbol + return classSymbol?.memberScope?.callables?.toList()?.mapNotNull { symbol -> + (symbol.psi as? KtCallableDeclaration)?.takeIf { + symbol.getImplementationStatus(classSymbol) == ImplementationStatus.CANNOT_BE_IMPLEMENTED + } + } ?: emptyList() + } + } + companion object { context(KaSession) fun getUnimplementedMembers(classWithUnimplementedMembers: KtClassOrObject): List = diff --git a/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/quickfix/K1QuickFixTestGenerated.java b/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/quickfix/K1QuickFixTestGenerated.java index d84616471400..bff125a42000 100644 --- a/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/quickfix/K1QuickFixTestGenerated.java +++ b/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/quickfix/K1QuickFixTestGenerated.java @@ -18707,6 +18707,11 @@ public abstract class K1QuickFixTestGenerated extends AbstractK1QuickFixTest { KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); } + @TestMetadata("hiddenVirtualMethod.kt") + public void testHiddenVirtualMethod() throws Exception { + runTest("testData/quickfix/typeMismatch/letImplementInterface/hiddenVirtualMethod.kt"); + } + @TestMetadata("kt25928.kt") public void testKt25928() throws Exception { runTest("testData/quickfix/typeMismatch/letImplementInterface/kt25928.kt"); diff --git a/plugins/kotlin/idea/tests/testData/quickfix/typeMismatch/letImplementInterface/hiddenVirtualMethod.k2.kt.after b/plugins/kotlin/idea/tests/testData/quickfix/typeMismatch/letImplementInterface/hiddenVirtualMethod.k2.kt.after new file mode 100644 index 000000000000..a9f98dc305b3 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/quickfix/typeMismatch/letImplementInterface/hiddenVirtualMethod.k2.kt.after @@ -0,0 +1,22 @@ +// "Let 'C' implement interface 'I'" "true" + +class C : I { + override fun exampleMethod() { + } +} + +interface I { + fun exampleMethod() +} + +fun acceptExampleInterface(i: I) { + i.exampleMethod() +} + +fun testCase(c: C) { + acceptExampleInterface(c) +} + +// IGNORE_K1 +// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.LetImplementInterfaceFix +// FUS_K2_QUICKFIX_NAME: org.jetbrains.kotlin.idea.k2.codeinsight.fixes.LetImplementInterfaceFixFactories$LetImplementInterfaceFix \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/quickfix/typeMismatch/letImplementInterface/hiddenVirtualMethod.kt b/plugins/kotlin/idea/tests/testData/quickfix/typeMismatch/letImplementInterface/hiddenVirtualMethod.kt new file mode 100644 index 000000000000..6d32c49a6f0b --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/quickfix/typeMismatch/letImplementInterface/hiddenVirtualMethod.kt @@ -0,0 +1,22 @@ +// "Let 'C' implement interface 'I'" "true" + +class C { + fun exampleMethod() { + } +} + +interface I { + fun exampleMethod() +} + +fun acceptExampleInterface(i: I) { + i.exampleMethod() +} + +fun testCase(c: C) { + acceptExampleInterface(c) +} + +// IGNORE_K1 +// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.LetImplementInterfaceFix +// FUS_K2_QUICKFIX_NAME: org.jetbrains.kotlin.idea.k2.codeinsight.fixes.LetImplementInterfaceFixFactories$LetImplementInterfaceFix \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/quickfix/typeMismatch/letImplementInterface/hiddenVirtualMethod.kt.after b/plugins/kotlin/idea/tests/testData/quickfix/typeMismatch/letImplementInterface/hiddenVirtualMethod.kt.after new file mode 100644 index 000000000000..9f422b6a520e --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/quickfix/typeMismatch/letImplementInterface/hiddenVirtualMethod.kt.after @@ -0,0 +1,23 @@ +// "Let 'C' implement interface 'I'" "true" +// ERROR: 'exampleMethod' hides member of supertype 'I' and needs 'override' modifier + +class C : I { + fun exampleMethod() { + } +} + +interface I { + fun exampleMethod() +} + +fun acceptExampleInterface(i: I) { + i.exampleMethod() +} + +fun testCase(c: C) { + acceptExampleInterface(c) +} + +// IGNORE_K1 +// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.LetImplementInterfaceFix +// FUS_K2_QUICKFIX_NAME: org.jetbrains.kotlin.idea.k2.codeinsight.fixes.LetImplementInterfaceFixFactories$LetImplementInterfaceFix \ No newline at end of file