Add quickfix for RETURN_TYPE_MISMATCH_ON_OVERRIDE

#KT-27972 Fixed
This commit is contained in:
Toshiaki Kameyama
2018-12-06 17:56:56 +09:00
committed by Natalia Selezneva
parent 683ed4eb95
commit 3040a2b145
12 changed files with 281 additions and 0 deletions
@@ -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<KtSuperTypeListEntry>(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)
}
}
}
@@ -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)
@@ -0,0 +1,18 @@
// "Change type argument to String" "true"
abstract class Foo<T1, T2> {
abstract fun foo1(): T1
abstract val foo2: T2
}
interface Bar<T1, T2> {
val bar1: T1
fun bar2(): T2
}
class Test : Foo<Int, Int>(), Bar<Int, Int> {
override fun foo1(): Int = 1
override val foo2: <caret>String = "2"
override val bar1: Int = 3
override fun bar2(): Int = 4
}
@@ -0,0 +1,18 @@
// "Change type argument to String" "true"
abstract class Foo<T1, T2> {
abstract fun foo1(): T1
abstract val foo2: T2
}
interface Bar<T1, T2> {
val bar1: T1
fun bar2(): T2
}
class Test : Foo<Int, String>(), Bar<Int, Int> {
override fun foo1(): Int = 1
override val foo2: String = "2"
override val bar1: Int = 3
override fun bar2(): Int = 4
}
@@ -0,0 +1,18 @@
// "Change type argument to Long" "true"
abstract class Foo<T1, T2> {
abstract fun foo1(): T1
abstract val foo2: T2
}
interface Bar<T1, T2> {
val bar1: T1
fun bar2(): T2
}
class Test : Foo<Int, Int>(), Bar<Int, Int> {
override fun foo1(): Int = 1
override val foo2: Int = 2
override val <caret>bar1 = 3L
override fun bar2(): Int = 4
}
@@ -0,0 +1,18 @@
// "Change type argument to Long" "true"
abstract class Foo<T1, T2> {
abstract fun foo1(): T1
abstract val foo2: T2
}
interface Bar<T1, T2> {
val bar1: T1
fun bar2(): T2
}
class Test : Foo<Int, Int>(), Bar<Long, Int> {
override fun foo1(): Int = 1
override val foo2: Int = 2
override val bar1 = 3L
override fun bar2(): Int = 4
}
@@ -0,0 +1,18 @@
// "Change type argument to Long" "true"
abstract class Foo<T1, T2> {
abstract fun foo1(): T1
abstract val foo2: T2
}
interface Bar<T1, T2> {
val bar1: T1
fun bar2(): T2
}
class Test : Foo<Int, Int>(), Bar<Int, Int> {
override fun foo1(): <caret>Long = 1L
override val foo2: Int = 2
override val bar1: Int = 3
override fun bar2(): Int = 4
}
@@ -0,0 +1,18 @@
// "Change type argument to Long" "true"
abstract class Foo<T1, T2> {
abstract fun foo1(): T1
abstract val foo2: T2
}
interface Bar<T1, T2> {
val bar1: T1
fun bar2(): T2
}
class Test : Foo<Long, Int>(), Bar<Int, Int> {
override fun foo1(): Long = 1L
override val foo2: Int = 2
override val bar1: Int = 3
override fun bar2(): Int = 4
}
@@ -0,0 +1,18 @@
// "Change type argument to String" "true"
abstract class Foo<T1, T2> {
abstract fun foo1(): T1
abstract val foo2: T2
}
interface Bar<T1, T2> {
val bar1: T1
fun bar2(): T2
}
class Test : Foo<Int, Int>(), Bar<Int, Int> {
override fun foo1(): Int = 1
override val foo2: Int = 2
override val bar1: Int = 3
override fun <caret>bar2() = "4"
}
@@ -0,0 +1,18 @@
// "Change type argument to String" "true"
abstract class Foo<T1, T2> {
abstract fun foo1(): T1
abstract val foo2: T2
}
interface Bar<T1, T2> {
val bar1: T1
fun bar2(): T2
}
class Test : Foo<Int, Int>(), Bar<Int, String> {
override fun foo1(): Int = 1
override val foo2: Int = 2
override val bar1: Int = 3
override fun bar2() = "4"
}
@@ -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)
@@ -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)