diff --git a/plugins/kotlin/code-insight/fixes-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/fixes/AddDefaultConstructorFixFactory.kt b/plugins/kotlin/code-insight/fixes-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/fixes/AddDefaultConstructorFixFactory.kt new file mode 100644 index 000000000000..31aaee4f50d2 --- /dev/null +++ b/plugins/kotlin/code-insight/fixes-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/fixes/AddDefaultConstructorFixFactory.kt @@ -0,0 +1,61 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package org.jetbrains.kotlin.idea.k2.codeinsight.fixes + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.analysis.api.KaSession +import org.jetbrains.kotlin.analysis.api.fir.diagnostics.KaFirDiagnostic +import org.jetbrains.kotlin.analysis.api.symbols.KaClassKind +import org.jetbrains.kotlin.idea.codeinsight.api.applicators.fixes.KotlinQuickFixFactory +import org.jetbrains.kotlin.idea.quickfix.AddDefaultConstructorFix +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType + +internal object AddDefaultConstructorFixFactory { + + val addDefaultConstructorFixFactory = KotlinQuickFixFactory.ModCommandBased { diagnostic: KaFirDiagnostic.UnresolvedReference -> + val baseClass = elementToBaseClass(diagnostic.psi) ?: return@ModCommandBased emptyList() + + listOf( + AddDefaultConstructorFix(baseClass) + ) + } +} + +context(KaSession) +private fun elementToBaseClass(element: PsiElement): KtClass? { + return when { + element is KtConstructorCalleeExpression -> + element.getStrictParentOfType() + ?.superTypeListEntries + ?.asSequence() + ?.filterIsInstance() + ?.firstOrNull()?.let { + superTypeEntryToClass(it) + } + + + element is KtNameReferenceExpression && element.parent is KtUserType -> + element.getStrictParentOfType() + ?.let { + annotationEntryToClass(it) + } + + else -> null + } +} + +context(KaSession) +private fun superTypeEntryToClass(typeEntry: KtSuperTypeListEntry): KtClass? { + val baseType = typeEntry.typeReference?.type ?: return null + val baseClassSymbol = baseType.expandedSymbol ?: return null + if (!baseClassSymbol.isExpect) return null + if (baseClassSymbol.classKind != KaClassKind.CLASS) return null + return baseClassSymbol.psi as? KtClass +} + +context(KaSession) +private fun annotationEntryToClass(entry: KtAnnotationEntry): KtClass? { + val symbol = entry.typeReference?.type?.expandedSymbol ?: return null + if (!symbol.isExpect) return null + return symbol.psi as? KtClass +} diff --git a/plugins/kotlin/code-insight/fixes-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/fixes/KotlinK2QuickFixRegistrar.kt b/plugins/kotlin/code-insight/fixes-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/fixes/KotlinK2QuickFixRegistrar.kt index 1ab3df81729e..92c6d86a17ef 100644 --- a/plugins/kotlin/code-insight/fixes-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/fixes/KotlinK2QuickFixRegistrar.kt +++ b/plugins/kotlin/code-insight/fixes-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/fixes/KotlinK2QuickFixRegistrar.kt @@ -124,6 +124,7 @@ class KotlinK2QuickFixRegistrar : KotlinQuickFixRegistrar() { registerFactory(RenameModToRemFixFactory.forbiddenBinaryModFactory) registerFactory(AddEmptyArgumentListFixFactory.addEmptyArgumentListFixFactory) registerFactory(KaFirDiagnostic.UnresolvedLabel::class, CreateLabelFixFactory.createLabelFixFactory) + registerFactory(AddDefaultConstructorFixFactory.addDefaultConstructorFixFactory) } private val addAbstract = KtQuickFixesListBuilder.registerPsiQuickFix { 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 20edc4d631e5..285bedc2c1f8 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 @@ -5685,7 +5685,59 @@ public abstract class HighLevelQuickFixTestGenerated extends AbstractHighLevelQu } } + @RunWith(JUnit3RunnerWithInners.class) + @TestMetadata("../../../idea/tests/testData/quickfix/addDefaultConstructor") + public static class AddDefaultConstructor extends AbstractHighLevelQuickFixTest { + @java.lang.Override + @org.jetbrains.annotations.NotNull + public final KotlinPluginMode getPluginMode() { + return KotlinPluginMode.K2; + } + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + @TestMetadata("expect.kt") + public void testExpect() throws Exception { + runTest("../../../idea/tests/testData/quickfix/addDefaultConstructor/expect.kt"); + } + + @TestMetadata("expectAnnotation.kt") + public void testExpectAnnotation() throws Exception { + runTest("../../../idea/tests/testData/quickfix/addDefaultConstructor/expectAnnotation.kt"); + } + + @TestMetadata("expectAnnotation2.kt") + public void testExpectAnnotation2() throws Exception { + runTest("../../../idea/tests/testData/quickfix/addDefaultConstructor/expectAnnotation2.kt"); + } + + @TestMetadata("expectAnnotation3.kt") + public void testExpectAnnotation3() throws Exception { + runTest("../../../idea/tests/testData/quickfix/addDefaultConstructor/expectAnnotation3.kt"); + } + + @TestMetadata("expectAnnotationWithUnresolvedParams.kt") + public void testExpectAnnotationWithUnresolvedParams() throws Exception { + runTest("../../../idea/tests/testData/quickfix/addDefaultConstructor/expectAnnotationWithUnresolvedParams.kt"); + } + + @TestMetadata("expectInterface.kt") + public void testExpectInterface() throws Exception { + runTest("../../../idea/tests/testData/quickfix/addDefaultConstructor/expectInterface.kt"); + } + + @TestMetadata("expectWithUnresolvedParams.kt") + public void testExpectWithUnresolvedParams() throws Exception { + runTest("../../../idea/tests/testData/quickfix/addDefaultConstructor/expectWithUnresolvedParams.kt"); + } + + @TestMetadata("interface.kt") + public void testInterface() throws Exception { + runTest("../../../idea/tests/testData/quickfix/addDefaultConstructor/interface.kt"); + } + } @RunWith(JUnit3RunnerWithInners.class) @TestMetadata("../../../idea/tests/testData/quickfix/addElseBranchToIf") 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 f216b506158b..a06c43abfae0 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 @@ -703,11 +703,21 @@ public abstract class K1QuickFixTestGenerated extends AbstractK1QuickFixTest { runTest("testData/quickfix/addDefaultConstructor/expectAnnotation3.kt"); } + @TestMetadata("expectAnnotationWithUnresolvedParams.kt") + public void testExpectAnnotationWithUnresolvedParams() throws Exception { + runTest("testData/quickfix/addDefaultConstructor/expectAnnotationWithUnresolvedParams.kt"); + } + @TestMetadata("expectInterface.kt") public void testExpectInterface() throws Exception { runTest("testData/quickfix/addDefaultConstructor/expectInterface.kt"); } + @TestMetadata("expectWithUnresolvedParams.kt") + public void testExpectWithUnresolvedParams() throws Exception { + runTest("testData/quickfix/addDefaultConstructor/expectWithUnresolvedParams.kt"); + } + @TestMetadata("interface.kt") public void testInterface() throws Exception { runTest("testData/quickfix/addDefaultConstructor/interface.kt"); diff --git a/plugins/kotlin/idea/tests/testData/quickfix/addDefaultConstructor/expect.kt b/plugins/kotlin/idea/tests/testData/quickfix/addDefaultConstructor/expect.kt index 3241225dbed2..e4a44cd96593 100644 --- a/plugins/kotlin/idea/tests/testData/quickfix/addDefaultConstructor/expect.kt +++ b/plugins/kotlin/idea/tests/testData/quickfix/addDefaultConstructor/expect.kt @@ -6,4 +6,5 @@ expect open class A open class C : A() -// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.AddDefaultConstructorFix \ No newline at end of file +// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.AddDefaultConstructorFix +// FUS_K2_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.AddDefaultConstructorFix \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/quickfix/addDefaultConstructor/expect.kt.after b/plugins/kotlin/idea/tests/testData/quickfix/addDefaultConstructor/expect.kt.after index 0b81785af904..25f2807375d4 100644 --- a/plugins/kotlin/idea/tests/testData/quickfix/addDefaultConstructor/expect.kt.after +++ b/plugins/kotlin/idea/tests/testData/quickfix/addDefaultConstructor/expect.kt.after @@ -6,4 +6,5 @@ expect open class A() open class C : A() -// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.AddDefaultConstructorFix \ No newline at end of file +// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.AddDefaultConstructorFix +// FUS_K2_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.AddDefaultConstructorFix \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/quickfix/addDefaultConstructor/expectAnnotation.kt b/plugins/kotlin/idea/tests/testData/quickfix/addDefaultConstructor/expectAnnotation.kt index 174e0287047a..5451bb685c4b 100644 --- a/plugins/kotlin/idea/tests/testData/quickfix/addDefaultConstructor/expectAnnotation.kt +++ b/plugins/kotlin/idea/tests/testData/quickfix/addDefaultConstructor/expectAnnotation.kt @@ -6,4 +6,5 @@ expect annotation class Foo @Foo fun bar() {} -// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.AddDefaultConstructorFix \ No newline at end of file +// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.AddDefaultConstructorFix +// FUS_K2_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.AddDefaultConstructorFix \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/quickfix/addDefaultConstructor/expectAnnotation.kt.after b/plugins/kotlin/idea/tests/testData/quickfix/addDefaultConstructor/expectAnnotation.kt.after index 511d1773b1aa..f3f88167ada8 100644 --- a/plugins/kotlin/idea/tests/testData/quickfix/addDefaultConstructor/expectAnnotation.kt.after +++ b/plugins/kotlin/idea/tests/testData/quickfix/addDefaultConstructor/expectAnnotation.kt.after @@ -6,4 +6,5 @@ expect annotation class Foo() @Foo fun bar() {} -// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.AddDefaultConstructorFix \ No newline at end of file +// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.AddDefaultConstructorFix +// FUS_K2_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.AddDefaultConstructorFix \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/quickfix/addDefaultConstructor/expectAnnotation2.kt b/plugins/kotlin/idea/tests/testData/quickfix/addDefaultConstructor/expectAnnotation2.kt index 0c90dd219255..b798f85b29ff 100644 --- a/plugins/kotlin/idea/tests/testData/quickfix/addDefaultConstructor/expectAnnotation2.kt +++ b/plugins/kotlin/idea/tests/testData/quickfix/addDefaultConstructor/expectAnnotation2.kt @@ -6,4 +6,5 @@ expect annotation class Foo @Foo() fun bar() {} -// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.AddDefaultConstructorFix \ No newline at end of file +// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.AddDefaultConstructorFix +// FUS_K2_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.AddDefaultConstructorFix \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/quickfix/addDefaultConstructor/expectAnnotation2.kt.after b/plugins/kotlin/idea/tests/testData/quickfix/addDefaultConstructor/expectAnnotation2.kt.after index 5fd8001f3390..1a7ae97a8c39 100644 --- a/plugins/kotlin/idea/tests/testData/quickfix/addDefaultConstructor/expectAnnotation2.kt.after +++ b/plugins/kotlin/idea/tests/testData/quickfix/addDefaultConstructor/expectAnnotation2.kt.after @@ -6,4 +6,5 @@ expect annotation class Foo() @Foo() fun bar() {} -// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.AddDefaultConstructorFix \ No newline at end of file +// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.AddDefaultConstructorFix +// FUS_K2_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.AddDefaultConstructorFix \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/quickfix/addDefaultConstructor/expectAnnotationWithUnresolvedParams.kt b/plugins/kotlin/idea/tests/testData/quickfix/addDefaultConstructor/expectAnnotationWithUnresolvedParams.kt new file mode 100644 index 000000000000..55a23a738e84 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/quickfix/addDefaultConstructor/expectAnnotationWithUnresolvedParams.kt @@ -0,0 +1,12 @@ +// "Add default constructor to 'expect' class" "false" +// ENABLE_MULTIPLATFORM +// ERROR: Expected annotation class 'Foo' has no actual declaration in module light_idea_test_case for JVM +// ERROR: This class does not have a constructor +// ERROR: Unresolved reference: x +// ERROR: Unresolved reference: y +// ERROR: Unresolved reference: z + +expect annotation class Foo + +@Foo(x, y, z) +fun bar() {} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/quickfix/addDefaultConstructor/expectWithUnresolvedParams.kt b/plugins/kotlin/idea/tests/testData/quickfix/addDefaultConstructor/expectWithUnresolvedParams.kt new file mode 100644 index 000000000000..0a76d74a0038 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/quickfix/addDefaultConstructor/expectWithUnresolvedParams.kt @@ -0,0 +1,11 @@ +// "Add default constructor to 'expect' class" "false" +// ENABLE_MULTIPLATFORM +// ERROR: Expected class 'A' has no actual declaration in module light_idea_test_case for JVM +// ERROR: This class does not have a constructor +// ERROR: Unresolved reference: x +// ERROR: Unresolved reference: y +// ERROR: Unresolved reference: z + +expect open class A + +open class C : A(x, y, z) \ No newline at end of file diff --git a/plugins/kotlin/util/test-generator-fir/test/org/jetbrains/kotlin/fir/testGenerator/codeinsight/GenerateK2QuickFixTests.kt b/plugins/kotlin/util/test-generator-fir/test/org/jetbrains/kotlin/fir/testGenerator/codeinsight/GenerateK2QuickFixTests.kt index 95a1e6d2415e..07480a7f3151 100644 --- a/plugins/kotlin/util/test-generator-fir/test/org/jetbrains/kotlin/fir/testGenerator/codeinsight/GenerateK2QuickFixTests.kt +++ b/plugins/kotlin/util/test-generator-fir/test/org/jetbrains/kotlin/fir/testGenerator/codeinsight/GenerateK2QuickFixTests.kt @@ -66,7 +66,7 @@ internal fun MutableTWorkspace.generateK2FixTests() { model("$idea/quickfix/addConversionCall", pattern = pattern, isIgnored = true) model("$idea/quickfix/addCrossinline", pattern = pattern) model("$idea/quickfix/addDataModifier", pattern = pattern) - model("$idea/quickfix/addDefaultConstructor", pattern = pattern, isIgnored = true) + model("$idea/quickfix/addDefaultConstructor", pattern = pattern) model("$idea/quickfix/addElseBranchToIf", pattern = pattern) model("$idea/quickfix/addEmptyArgumentList", pattern = pattern) model("$idea/quickfix/addEqEqTrue", pattern = pattern)