Add quickfix for RETURN_TYPE_MISMATCH_ON_OVERRIDE
#KT-27972 Fixed
This commit is contained in:
committed by
Natalia Selezneva
parent
683ed4eb95
commit
3040a2b145
+88
@@ -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)
|
COMPARE_TO_TYPE_MISMATCH.registerFactory(ChangeCallableReturnTypeFix.CompareToTypeMismatchFactory)
|
||||||
IMPLICIT_NOTHING_RETURN_TYPE.registerFactory(ChangeCallableReturnTypeFix.ChangingReturnTypeToNothingFactory)
|
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)
|
TOO_MANY_ARGUMENTS.registerFactory(ChangeFunctionSignatureFix)
|
||||||
NO_VALUE_FOR_PARAMETER.registerFactory(ChangeFunctionSignatureFix)
|
NO_VALUE_FOR_PARAMETER.registerFactory(ChangeFunctionSignatureFix)
|
||||||
UNUSED_PARAMETER.registerFactory(RemoveUnusedFunctionParameterFix)
|
UNUSED_PARAMETER.registerFactory(RemoveUnusedFunctionParameterFix)
|
||||||
|
|||||||
Vendored
+18
@@ -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
|
||||||
|
}
|
||||||
Vendored
+18
@@ -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
|
||||||
|
}
|
||||||
Vendored
+18
@@ -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
|
||||||
|
}
|
||||||
idea/testData/quickfix/changeSuperTypeListEntryTypeArgument/propertyTypeMismatchOnOverride2.kt.after
Vendored
+18
@@ -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
|
||||||
|
}
|
||||||
Vendored
+18
@@ -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
|
||||||
|
}
|
||||||
Vendored
+18
@@ -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
|
||||||
|
}
|
||||||
Vendored
+18
@@ -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"
|
||||||
|
}
|
||||||
Vendored
+18
@@ -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"
|
||||||
|
}
|
||||||
+13
@@ -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")
|
@TestMetadata("idea/testData/quickfix/changeToLabeledReturn")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@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")
|
@TestMetadata("idea/testData/quickfix/changeToLabeledReturn")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
Reference in New Issue
Block a user