From 3040a2b14597453820f5cce6fd1bd3c264820dfa Mon Sep 17 00:00:00 2001 From: Toshiaki Kameyama Date: Thu, 6 Dec 2018 17:56:56 +0900 Subject: [PATCH] Add quickfix for RETURN_TYPE_MISMATCH_ON_OVERRIDE #KT-27972 Fixed --- ...ChangeSuperTypeListEntryTypeArgumentFix.kt | 88 +++++++++++++++++++ .../kotlin/idea/quickfix/QuickFixRegistrar.kt | 3 + .../propertyTypeMismatchOnOverride.kt | 18 ++++ .../propertyTypeMismatchOnOverride.kt.after | 18 ++++ .../propertyTypeMismatchOnOverride2.kt | 18 ++++ .../propertyTypeMismatchOnOverride2.kt.after | 18 ++++ .../returnTypeMismatchOnOverride.kt | 18 ++++ .../returnTypeMismatchOnOverride.kt.after | 18 ++++ .../returnTypeMismatchOnOverride2.kt | 18 ++++ .../returnTypeMismatchOnOverride2.kt.after | 18 ++++ .../QuickFixMultiFileTestGenerated.java | 13 +++ .../idea/quickfix/QuickFixTestGenerated.java | 33 +++++++ 12 files changed, 281 insertions(+) create mode 100644 idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeSuperTypeListEntryTypeArgumentFix.kt create mode 100644 idea/testData/quickfix/changeSuperTypeListEntryTypeArgument/propertyTypeMismatchOnOverride.kt create mode 100644 idea/testData/quickfix/changeSuperTypeListEntryTypeArgument/propertyTypeMismatchOnOverride.kt.after create mode 100644 idea/testData/quickfix/changeSuperTypeListEntryTypeArgument/propertyTypeMismatchOnOverride2.kt create mode 100644 idea/testData/quickfix/changeSuperTypeListEntryTypeArgument/propertyTypeMismatchOnOverride2.kt.after create mode 100644 idea/testData/quickfix/changeSuperTypeListEntryTypeArgument/returnTypeMismatchOnOverride.kt create mode 100644 idea/testData/quickfix/changeSuperTypeListEntryTypeArgument/returnTypeMismatchOnOverride.kt.after create mode 100644 idea/testData/quickfix/changeSuperTypeListEntryTypeArgument/returnTypeMismatchOnOverride2.kt create mode 100644 idea/testData/quickfix/changeSuperTypeListEntryTypeArgument/returnTypeMismatchOnOverride2.kt.after diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeSuperTypeListEntryTypeArgumentFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeSuperTypeListEntryTypeArgumentFix.kt new file mode 100644 index 00000000000..ac757b4bcb7 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeSuperTypeListEntryTypeArgumentFix.kt @@ -0,0 +1,88 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.idea.quickfix + +import com.intellij.codeInsight.intention.IntentionAction +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.project.Project +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.getReturnTypeReference +import org.jetbrains.kotlin.idea.references.mainReference +import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.containingClass +import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils + +class ChangeSuperTypeListEntryTypeArgumentFix( + element: KtSuperTypeListEntry, + private val type: String, + private val typeArgumentIndex: Int +) : KotlinQuickFixAction(element) { + + override fun getText() = "Change type argument to $type" + + override fun getFamilyName() = text + + override fun invoke(project: Project, editor: Editor?, file: KtFile) { + val superTypeListEntry = element ?: return + + val typeArgumentList = superTypeListEntry.typeAsUserType?.typeArgumentList?.arguments?.mapIndexed { index, typeProjection -> + if (index == typeArgumentIndex) type else typeProjection.text + }?.joinToString(prefix = "<", postfix = ">", separator = ", ") { it } ?: return + + val psiFactory = KtPsiFactory(superTypeListEntry) + val newElement = when (superTypeListEntry) { + is KtSuperTypeEntry -> { + val classReference = superTypeListEntry.typeAsUserType?.referenceExpression?.text ?: return + psiFactory.createSuperTypeEntry("$classReference$typeArgumentList") + } + is KtSuperTypeCallEntry -> { + val classReference = superTypeListEntry.calleeExpression.constructorReferenceExpression?.text ?: return + val valueArgumentList = superTypeListEntry.valueArgumentList?.text ?: return + psiFactory.createSuperTypeCallEntry("$classReference$typeArgumentList$valueArgumentList") + } + else -> return + } + + superTypeListEntry.replace(newElement) + } + + companion object : KotlinSingleIntentionActionFactory() { + override fun createAction(diagnostic: Diagnostic): IntentionAction? { + val casted = when (diagnostic.factory) { + Errors.RETURN_TYPE_MISMATCH_ON_OVERRIDE -> Errors.RETURN_TYPE_MISMATCH_ON_OVERRIDE.cast(diagnostic) + Errors.PROPERTY_TYPE_MISMATCH_ON_OVERRIDE -> Errors.PROPERTY_TYPE_MISMATCH_ON_OVERRIDE.cast(diagnostic) + else -> null + } ?: return null + + val type = casted.a.returnType?.toString() ?: return null + + val superClassDescriptor = casted.b.containingDeclaration as? ClassDescriptor ?: return null + val superDeclaration = DescriptorToSourceUtils.descriptorToDeclaration(casted.b) as? KtNamedDeclaration ?: return null + val superTypeReference = superDeclaration.getReturnTypeReference()?.text ?: return null + val typeParameterIndex = superClassDescriptor.declaredTypeParameters.map { it.name.asString() }.indexOf(superTypeReference) + if (typeParameterIndex < 0) return null + + val containingClass = casted.psiElement.containingClass() ?: return null + val superTypeListEntry = containingClass.superTypeListEntries.find { + when (it) { + is KtSuperTypeEntry -> { + (it.typeAsUserType?.referenceExpression?.mainReference?.resolve() as? KtClass)?.descriptor == superClassDescriptor + } + is KtSuperTypeCallEntry -> { + it.calleeExpression.resolveToCall()?.resultingDescriptor?.returnType?.constructor?.declarationDescriptor == superClassDescriptor + } + else -> false + } + } ?: return null + + return ChangeSuperTypeListEntryTypeArgumentFix(superTypeListEntry, type, typeParameterIndex) + } + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt index f55e1d3a100..da4ba0133e0 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt @@ -309,6 +309,9 @@ class QuickFixRegistrar : QuickFixContributor { COMPARE_TO_TYPE_MISMATCH.registerFactory(ChangeCallableReturnTypeFix.CompareToTypeMismatchFactory) IMPLICIT_NOTHING_RETURN_TYPE.registerFactory(ChangeCallableReturnTypeFix.ChangingReturnTypeToNothingFactory) + RETURN_TYPE_MISMATCH_ON_OVERRIDE.registerFactory(ChangeSuperTypeListEntryTypeArgumentFix) + PROPERTY_TYPE_MISMATCH_ON_OVERRIDE.registerFactory(ChangeSuperTypeListEntryTypeArgumentFix) + TOO_MANY_ARGUMENTS.registerFactory(ChangeFunctionSignatureFix) NO_VALUE_FOR_PARAMETER.registerFactory(ChangeFunctionSignatureFix) UNUSED_PARAMETER.registerFactory(RemoveUnusedFunctionParameterFix) diff --git a/idea/testData/quickfix/changeSuperTypeListEntryTypeArgument/propertyTypeMismatchOnOverride.kt b/idea/testData/quickfix/changeSuperTypeListEntryTypeArgument/propertyTypeMismatchOnOverride.kt new file mode 100644 index 00000000000..8b859ccd176 --- /dev/null +++ b/idea/testData/quickfix/changeSuperTypeListEntryTypeArgument/propertyTypeMismatchOnOverride.kt @@ -0,0 +1,18 @@ +// "Change type argument to String" "true" +abstract class Foo { + abstract fun foo1(): T1 + abstract val foo2: T2 +} + +interface Bar { + val bar1: T1 + fun bar2(): T2 +} + +class Test : Foo(), Bar { + override fun foo1(): Int = 1 + override val foo2: String = "2" + + override val bar1: Int = 3 + override fun bar2(): Int = 4 +} \ No newline at end of file diff --git a/idea/testData/quickfix/changeSuperTypeListEntryTypeArgument/propertyTypeMismatchOnOverride.kt.after b/idea/testData/quickfix/changeSuperTypeListEntryTypeArgument/propertyTypeMismatchOnOverride.kt.after new file mode 100644 index 00000000000..cc002528538 --- /dev/null +++ b/idea/testData/quickfix/changeSuperTypeListEntryTypeArgument/propertyTypeMismatchOnOverride.kt.after @@ -0,0 +1,18 @@ +// "Change type argument to String" "true" +abstract class Foo { + abstract fun foo1(): T1 + abstract val foo2: T2 +} + +interface Bar { + val bar1: T1 + fun bar2(): T2 +} + +class Test : Foo(), Bar { + override fun foo1(): Int = 1 + override val foo2: String = "2" + + override val bar1: Int = 3 + override fun bar2(): Int = 4 +} \ No newline at end of file diff --git a/idea/testData/quickfix/changeSuperTypeListEntryTypeArgument/propertyTypeMismatchOnOverride2.kt b/idea/testData/quickfix/changeSuperTypeListEntryTypeArgument/propertyTypeMismatchOnOverride2.kt new file mode 100644 index 00000000000..ce9ad7d9b1a --- /dev/null +++ b/idea/testData/quickfix/changeSuperTypeListEntryTypeArgument/propertyTypeMismatchOnOverride2.kt @@ -0,0 +1,18 @@ +// "Change type argument to Long" "true" +abstract class Foo { + abstract fun foo1(): T1 + abstract val foo2: T2 +} + +interface Bar { + val bar1: T1 + fun bar2(): T2 +} + +class Test : Foo(), Bar { + override fun foo1(): Int = 1 + override val foo2: Int = 2 + + override val bar1 = 3L + override fun bar2(): Int = 4 +} \ No newline at end of file diff --git a/idea/testData/quickfix/changeSuperTypeListEntryTypeArgument/propertyTypeMismatchOnOverride2.kt.after b/idea/testData/quickfix/changeSuperTypeListEntryTypeArgument/propertyTypeMismatchOnOverride2.kt.after new file mode 100644 index 00000000000..3835d87df9b --- /dev/null +++ b/idea/testData/quickfix/changeSuperTypeListEntryTypeArgument/propertyTypeMismatchOnOverride2.kt.after @@ -0,0 +1,18 @@ +// "Change type argument to Long" "true" +abstract class Foo { + abstract fun foo1(): T1 + abstract val foo2: T2 +} + +interface Bar { + val bar1: T1 + fun bar2(): T2 +} + +class Test : Foo(), Bar { + override fun foo1(): Int = 1 + override val foo2: Int = 2 + + override val bar1 = 3L + override fun bar2(): Int = 4 +} \ No newline at end of file diff --git a/idea/testData/quickfix/changeSuperTypeListEntryTypeArgument/returnTypeMismatchOnOverride.kt b/idea/testData/quickfix/changeSuperTypeListEntryTypeArgument/returnTypeMismatchOnOverride.kt new file mode 100644 index 00000000000..cd1c1d80565 --- /dev/null +++ b/idea/testData/quickfix/changeSuperTypeListEntryTypeArgument/returnTypeMismatchOnOverride.kt @@ -0,0 +1,18 @@ +// "Change type argument to Long" "true" +abstract class Foo { + abstract fun foo1(): T1 + abstract val foo2: T2 +} + +interface Bar { + val bar1: T1 + fun bar2(): T2 +} + +class Test : Foo(), Bar { + override fun foo1(): Long = 1L + override val foo2: Int = 2 + + override val bar1: Int = 3 + override fun bar2(): Int = 4 +} \ No newline at end of file diff --git a/idea/testData/quickfix/changeSuperTypeListEntryTypeArgument/returnTypeMismatchOnOverride.kt.after b/idea/testData/quickfix/changeSuperTypeListEntryTypeArgument/returnTypeMismatchOnOverride.kt.after new file mode 100644 index 00000000000..c233b7f6d5b --- /dev/null +++ b/idea/testData/quickfix/changeSuperTypeListEntryTypeArgument/returnTypeMismatchOnOverride.kt.after @@ -0,0 +1,18 @@ +// "Change type argument to Long" "true" +abstract class Foo { + abstract fun foo1(): T1 + abstract val foo2: T2 +} + +interface Bar { + val bar1: T1 + fun bar2(): T2 +} + +class Test : Foo(), Bar { + override fun foo1(): Long = 1L + override val foo2: Int = 2 + + override val bar1: Int = 3 + override fun bar2(): Int = 4 +} \ No newline at end of file diff --git a/idea/testData/quickfix/changeSuperTypeListEntryTypeArgument/returnTypeMismatchOnOverride2.kt b/idea/testData/quickfix/changeSuperTypeListEntryTypeArgument/returnTypeMismatchOnOverride2.kt new file mode 100644 index 00000000000..502fcc3e97c --- /dev/null +++ b/idea/testData/quickfix/changeSuperTypeListEntryTypeArgument/returnTypeMismatchOnOverride2.kt @@ -0,0 +1,18 @@ +// "Change type argument to String" "true" +abstract class Foo { + abstract fun foo1(): T1 + abstract val foo2: T2 +} + +interface Bar { + val bar1: T1 + fun bar2(): T2 +} + +class Test : Foo(), Bar { + override fun foo1(): Int = 1 + override val foo2: Int = 2 + + override val bar1: Int = 3 + override fun bar2() = "4" +} \ No newline at end of file diff --git a/idea/testData/quickfix/changeSuperTypeListEntryTypeArgument/returnTypeMismatchOnOverride2.kt.after b/idea/testData/quickfix/changeSuperTypeListEntryTypeArgument/returnTypeMismatchOnOverride2.kt.after new file mode 100644 index 00000000000..ef91df25803 --- /dev/null +++ b/idea/testData/quickfix/changeSuperTypeListEntryTypeArgument/returnTypeMismatchOnOverride2.kt.after @@ -0,0 +1,18 @@ +// "Change type argument to String" "true" +abstract class Foo { + abstract fun foo1(): T1 + abstract val foo2: T2 +} + +interface Bar { + val bar1: T1 + fun bar2(): T2 +} + +class Test : Foo(), Bar { + override fun foo1(): Int = 1 + override val foo2: Int = 2 + + override val bar1: Int = 3 + override fun bar2() = "4" +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java index 1498f1e8943..7d02fe3ded0 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java @@ -1226,6 +1226,19 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes } } + @TestMetadata("idea/testData/quickfix/changeSuperTypeListEntryTypeArgument") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ChangeSuperTypeListEntryTypeArgument extends AbstractQuickFixMultiFileTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTestWithExtraFile, TargetBackend.ANY, testDataFilePath); + } + + public void testAllFilesPresentInChangeSuperTypeListEntryTypeArgument() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/changeSuperTypeListEntryTypeArgument"), Pattern.compile("^(\\w+)\\.((before\\.Main\\.\\w+)|(test))$"), TargetBackend.ANY, true); + } + } + @TestMetadata("idea/testData/quickfix/changeToLabeledReturn") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java index cddbfc5227b..33ff695a891 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -1966,6 +1966,39 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { } } + @TestMetadata("idea/testData/quickfix/changeSuperTypeListEntryTypeArgument") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ChangeSuperTypeListEntryTypeArgument extends AbstractQuickFixTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath); + } + + public void testAllFilesPresentInChangeSuperTypeListEntryTypeArgument() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/changeSuperTypeListEntryTypeArgument"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("propertyTypeMismatchOnOverride.kt") + public void testPropertyTypeMismatchOnOverride() throws Exception { + runTest("idea/testData/quickfix/changeSuperTypeListEntryTypeArgument/propertyTypeMismatchOnOverride.kt"); + } + + @TestMetadata("propertyTypeMismatchOnOverride2.kt") + public void testPropertyTypeMismatchOnOverride2() throws Exception { + runTest("idea/testData/quickfix/changeSuperTypeListEntryTypeArgument/propertyTypeMismatchOnOverride2.kt"); + } + + @TestMetadata("returnTypeMismatchOnOverride.kt") + public void testReturnTypeMismatchOnOverride() throws Exception { + runTest("idea/testData/quickfix/changeSuperTypeListEntryTypeArgument/returnTypeMismatchOnOverride.kt"); + } + + @TestMetadata("returnTypeMismatchOnOverride2.kt") + public void testReturnTypeMismatchOnOverride2() throws Exception { + runTest("idea/testData/quickfix/changeSuperTypeListEntryTypeArgument/returnTypeMismatchOnOverride2.kt"); + } + } + @TestMetadata("idea/testData/quickfix/changeToLabeledReturn") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)