diff --git a/plugins/kotlin/code-insight/fixes-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/fixes/ConvertClassToKClassFixFactories.kt b/plugins/kotlin/code-insight/fixes-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/fixes/ConvertClassToKClassFixFactories.kt new file mode 100644 index 000000000000..b3a522eb7388 --- /dev/null +++ b/plugins/kotlin/code-insight/fixes-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/fixes/ConvertClassToKClassFixFactories.kt @@ -0,0 +1,58 @@ +// 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.types.KaType +import org.jetbrains.kotlin.idea.codeinsight.api.applicators.fixes.KotlinQuickFixFactory +import org.jetbrains.kotlin.idea.quickfix.ConvertClassToKClassFix +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.StandardClassIds +import org.jetbrains.kotlin.psi.KtDotQualifiedExpression +import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.psi.KtProperty + +internal object ConvertClassToKClassFixFactories { + + val ArgumentTypeMismatchFixFactory = KotlinQuickFixFactory.ModCommandBased { diagnostic: KaFirDiagnostic.ArgumentTypeMismatch -> + listOfNotNull(createFixIfAvailable(diagnostic.psi, diagnostic.expectedType)) + } + + val ReturnTypeMismatchFixFactory = KotlinQuickFixFactory.ModCommandBased { diagnostic: KaFirDiagnostic.ReturnTypeMismatch -> + listOfNotNull(createFixIfAvailable(diagnostic.psi, diagnostic.expectedType)) + } + + val InitializerTypeMismatchFixFactory = KotlinQuickFixFactory.ModCommandBased { diagnostic: KaFirDiagnostic.InitializerTypeMismatch -> + listOfNotNull(createFixIfAvailable((diagnostic.psi as? KtProperty)?.initializer, diagnostic.expectedType)) + } + + val AssignmentTypeMismatchFixFactory = KotlinQuickFixFactory.ModCommandBased { diagnostic: KaFirDiagnostic.AssignmentTypeMismatch -> + listOfNotNull(createFixIfAvailable(diagnostic.psi, diagnostic.expectedType)) + } + + context(KaSession) + private fun createFixIfAvailable(element: PsiElement?, expectedType: KaType): ConvertClassToKClassFix? { + val dotQualifiedExpression = element as? KtDotQualifiedExpression ?: return null + if (!expectedType.isKClass()) return null + + val expressionType = dotQualifiedExpression.expressionType ?: return null + if (!expressionType.isJClass()) return null + + val children = dotQualifiedExpression.children + if (children.size != 2) return null + + val firstChild = children.first() as? KtExpression ?: return null + val firstChildType = firstChild.expressionType ?: return null + + if (!firstChildType.isSubtypeOf(expectedType)) return null + + return ConvertClassToKClassFix(dotQualifiedExpression) + } + + context(KaSession) + private fun KaType.isKClass(): Boolean = isClassType(StandardClassIds.KClass) + + context(KaSession) + private fun KaType.isJClass(): Boolean = isClassType(ClassId.fromString("java/lang/Class")) +} 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 26bfc1f4b78e..4b8ee1a9d1b4 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 @@ -145,6 +145,10 @@ class KotlinK2QuickFixRegistrar : KotlinQuickFixRegistrar() { registerFactory(ExtensionPropertyWithBackingFieldFixFactories.convertToGetterFixFactory) registerFactory(OverrideDeprecationFixFactories.copyDeprecatedAnnotationFixFactory) registerFactory(TypeVarianceConflictErrorFixFactories.addUnsafeVarianceAnnotationFixFactory) + registerFactory(ConvertClassToKClassFixFactories.ArgumentTypeMismatchFixFactory) + registerFactory(ConvertClassToKClassFixFactories.ReturnTypeMismatchFixFactory) + registerFactory(ConvertClassToKClassFixFactories.InitializerTypeMismatchFixFactory) + registerFactory(ConvertClassToKClassFixFactories.AssignmentTypeMismatchFixFactory) } 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 809fe4d39972..2fba131eaecd 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 @@ -10813,7 +10813,44 @@ public abstract class HighLevelQuickFixTestGenerated extends AbstractHighLevelQu } } + @RunWith(JUnit3RunnerWithInners.class) + @TestMetadata("../../../idea/tests/testData/quickfix/typeInferenceExpectedTypeMismatch") + public static class TypeInferenceExpectedTypeMismatch 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("argument.kt") + public void testArgument() throws Exception { + runTest("../../../idea/tests/testData/quickfix/typeInferenceExpectedTypeMismatch/argument.kt"); + } + + @TestMetadata("assignment.kt") + public void testAssignment() throws Exception { + runTest("../../../idea/tests/testData/quickfix/typeInferenceExpectedTypeMismatch/assignment.kt"); + } + + @TestMetadata("initializer.kt") + public void testInitializer() throws Exception { + runTest("../../../idea/tests/testData/quickfix/typeInferenceExpectedTypeMismatch/initializer.kt"); + } + + @TestMetadata("notKClass.kt") + public void testNotKClass() throws Exception { + runTest("../../../idea/tests/testData/quickfix/typeInferenceExpectedTypeMismatch/notKClass.kt"); + } + + @TestMetadata("return.kt") + public void testReturn() throws Exception { + runTest("../../../idea/tests/testData/quickfix/typeInferenceExpectedTypeMismatch/return.kt"); + } + } @RunWith(JUnit3RunnerWithInners.class) @TestMetadata("../../../idea/tests/testData/quickfix/typeOfAnnotationMember") 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 3a8ad88dd383..4dd01d10f4cc 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 @@ -17646,9 +17646,29 @@ public abstract class K1QuickFixTestGenerated extends AbstractK1QuickFixTest { KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); } - @TestMetadata("convertClassToKClass1Runtime.kt") - public void testConvertClassToKClass1Runtime() throws Exception { - runTest("testData/quickfix/typeInferenceExpectedTypeMismatch/convertClassToKClass1Runtime.kt"); + @TestMetadata("argument.kt") + public void testArgument() throws Exception { + runTest("testData/quickfix/typeInferenceExpectedTypeMismatch/argument.kt"); + } + + @TestMetadata("assignment.kt") + public void testAssignment() throws Exception { + runTest("testData/quickfix/typeInferenceExpectedTypeMismatch/assignment.kt"); + } + + @TestMetadata("initializer.kt") + public void testInitializer() throws Exception { + runTest("testData/quickfix/typeInferenceExpectedTypeMismatch/initializer.kt"); + } + + @TestMetadata("notKClass.kt") + public void testNotKClass() throws Exception { + runTest("testData/quickfix/typeInferenceExpectedTypeMismatch/notKClass.kt"); + } + + @TestMetadata("return.kt") + public void testReturn() throws Exception { + runTest("testData/quickfix/typeInferenceExpectedTypeMismatch/return.kt"); } } diff --git a/plugins/kotlin/idea/tests/testData/quickfix/typeInferenceExpectedTypeMismatch/convertClassToKClass1Runtime.kt b/plugins/kotlin/idea/tests/testData/quickfix/typeInferenceExpectedTypeMismatch/argument.kt similarity index 67% rename from plugins/kotlin/idea/tests/testData/quickfix/typeInferenceExpectedTypeMismatch/convertClassToKClass1Runtime.kt rename to plugins/kotlin/idea/tests/testData/quickfix/typeInferenceExpectedTypeMismatch/argument.kt index ee12db130b61..ee05363b32e3 100644 --- a/plugins/kotlin/idea/tests/testData/quickfix/typeInferenceExpectedTypeMismatch/convertClassToKClass1Runtime.kt +++ b/plugins/kotlin/idea/tests/testData/quickfix/typeInferenceExpectedTypeMismatch/argument.kt @@ -8,4 +8,5 @@ class Foo fun bar(kc: kotlin.reflect.KClass) { } -// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ConvertClassToKClassFix \ No newline at end of file +// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ConvertClassToKClassFix +// FUS_K2_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ConvertClassToKClassFix \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/quickfix/typeInferenceExpectedTypeMismatch/convertClassToKClass1Runtime.kt.after b/plugins/kotlin/idea/tests/testData/quickfix/typeInferenceExpectedTypeMismatch/argument.kt.after similarity index 66% rename from plugins/kotlin/idea/tests/testData/quickfix/typeInferenceExpectedTypeMismatch/convertClassToKClass1Runtime.kt.after rename to plugins/kotlin/idea/tests/testData/quickfix/typeInferenceExpectedTypeMismatch/argument.kt.after index ead2b1813a77..9815a1bf7805 100644 --- a/plugins/kotlin/idea/tests/testData/quickfix/typeInferenceExpectedTypeMismatch/convertClassToKClass1Runtime.kt.after +++ b/plugins/kotlin/idea/tests/testData/quickfix/typeInferenceExpectedTypeMismatch/argument.kt.after @@ -8,4 +8,5 @@ class Foo fun bar(kc: kotlin.reflect.KClass) { } -// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ConvertClassToKClassFix \ No newline at end of file +// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ConvertClassToKClassFix +// FUS_K2_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ConvertClassToKClassFix \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/quickfix/typeInferenceExpectedTypeMismatch/assignment.kt b/plugins/kotlin/idea/tests/testData/quickfix/typeInferenceExpectedTypeMismatch/assignment.kt new file mode 100644 index 000000000000..c791e125b360 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/quickfix/typeInferenceExpectedTypeMismatch/assignment.kt @@ -0,0 +1,10 @@ +// "Remove '.java'" "true" +// WITH_STDLIB +fun foo() { + val clazz: kotlin.reflect.KClass + clazz = Foo::class.java +} + +class Foo +// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ConvertClassToKClassFix +// FUS_K2_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ConvertClassToKClassFix \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/quickfix/typeInferenceExpectedTypeMismatch/assignment.kt.after b/plugins/kotlin/idea/tests/testData/quickfix/typeInferenceExpectedTypeMismatch/assignment.kt.after new file mode 100644 index 000000000000..62e61bdd0968 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/quickfix/typeInferenceExpectedTypeMismatch/assignment.kt.after @@ -0,0 +1,10 @@ +// "Remove '.java'" "true" +// WITH_STDLIB +fun foo() { + val clazz: kotlin.reflect.KClass + clazz = Foo::class +} + +class Foo +// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ConvertClassToKClassFix +// FUS_K2_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ConvertClassToKClassFix \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/quickfix/typeInferenceExpectedTypeMismatch/initializer.kt b/plugins/kotlin/idea/tests/testData/quickfix/typeInferenceExpectedTypeMismatch/initializer.kt new file mode 100644 index 000000000000..9eb19e28a253 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/quickfix/typeInferenceExpectedTypeMismatch/initializer.kt @@ -0,0 +1,9 @@ +// "Remove '.java'" "true" +// WITH_STDLIB +fun foo() { + val clazz: kotlin.reflect.KClass = Foo::class.java +} + +class Foo +// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ConvertClassToKClassFix +// FUS_K2_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ConvertClassToKClassFix \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/quickfix/typeInferenceExpectedTypeMismatch/initializer.kt.after b/plugins/kotlin/idea/tests/testData/quickfix/typeInferenceExpectedTypeMismatch/initializer.kt.after new file mode 100644 index 000000000000..e162b7cb6806 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/quickfix/typeInferenceExpectedTypeMismatch/initializer.kt.after @@ -0,0 +1,9 @@ +// "Remove '.java'" "true" +// WITH_STDLIB +fun foo() { + val clazz: kotlin.reflect.KClass = Foo::class +} + +class Foo +// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ConvertClassToKClassFix +// FUS_K2_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ConvertClassToKClassFix \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/quickfix/typeInferenceExpectedTypeMismatch/notKClass.kt b/plugins/kotlin/idea/tests/testData/quickfix/typeInferenceExpectedTypeMismatch/notKClass.kt new file mode 100644 index 000000000000..d16cbfe0ea1c --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/quickfix/typeInferenceExpectedTypeMismatch/notKClass.kt @@ -0,0 +1,11 @@ +// "Remove '.java'" "false" +// WITH_STDLIB +// ERROR: Type mismatch: inferred type is Class but String was expected +fun foo() { + bar(Foo::class.java) +} + +class Foo + +fun bar(s: String) { +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/quickfix/typeInferenceExpectedTypeMismatch/return.kt b/plugins/kotlin/idea/tests/testData/quickfix/typeInferenceExpectedTypeMismatch/return.kt new file mode 100644 index 000000000000..6e5dc7dcbedf --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/quickfix/typeInferenceExpectedTypeMismatch/return.kt @@ -0,0 +1,9 @@ +// "Remove '.java'" "true" +// WITH_STDLIB +fun foo(): kotlin.reflect.KClass { + return Foo::class.java +} + +class Foo +// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ConvertClassToKClassFix +// FUS_K2_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ConvertClassToKClassFix \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/quickfix/typeInferenceExpectedTypeMismatch/return.kt.after b/plugins/kotlin/idea/tests/testData/quickfix/typeInferenceExpectedTypeMismatch/return.kt.after new file mode 100644 index 000000000000..faecfdf56001 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/quickfix/typeInferenceExpectedTypeMismatch/return.kt.after @@ -0,0 +1,9 @@ +// "Remove '.java'" "true" +// WITH_STDLIB +fun foo(): kotlin.reflect.KClass { + return Foo::class +} + +class Foo +// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ConvertClassToKClassFix +// FUS_K2_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ConvertClassToKClassFix \ 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 2cd9ab4557ca..3d51ea315572 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 @@ -193,7 +193,7 @@ internal fun MutableTWorkspace.generateK2FixTests() { model("$idea/quickfix/suspiciousCollectionReassignment", pattern = pattern, isIgnored = true) model("$idea/quickfix/tooLongCharLiteralToString", pattern = pattern) model("$idea/quickfix/typeImports", pattern = pattern) - model("$idea/quickfix/typeInferenceExpectedTypeMismatch", pattern = pattern, isIgnored = true) + model("$idea/quickfix/typeInferenceExpectedTypeMismatch", pattern = pattern) model("$idea/quickfix/typeOfAnnotationMember", pattern = pattern) model("$idea/quickfix/typeParameters", pattern = pattern) model("$idea/quickfix/typeProjection", pattern = pattern, isIgnored = true)