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)
|
||||
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)
|
||||
|
||||
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")
|
||||
@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)
|
||||
|
||||
Reference in New Issue
Block a user