Add quick fix for AMBIGUOUS_SUPER
#KT-5271 Fixed
This commit is contained in:
committed by
Yan Zhulanow
parent
7c0af78b08
commit
f6a13df388
@@ -644,6 +644,8 @@ class QuickFixRegistrar : QuickFixContributor {
|
||||
|
||||
TOO_MANY_ARGUMENTS.registerFactory(RemoveArgumentFix)
|
||||
|
||||
AMBIGUOUS_SUPER.registerFactory(SpecifySuperTypeFix)
|
||||
|
||||
FUN_INTERFACE_WRONG_COUNT_OF_ABSTRACT_MEMBERS.registerFactory(RemoveModifierFix.createRemoveFunFromInterfaceFactory())
|
||||
|
||||
TOPLEVEL_TYPEALIASES_ONLY.registerFactory(MoveTypeAliasToTopLevelFix)
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.openapi.command.CommandProcessor
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.ui.popup.JBPopupFactory
|
||||
import com.intellij.openapi.ui.popup.ListPopupStep
|
||||
import com.intellij.openapi.ui.popup.PopupStep
|
||||
import com.intellij.openapi.ui.popup.util.BaseListPopupStep
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.idea.analysis.analyzeAsReplacement
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.refactoring.fqName.fqName
|
||||
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForReceiver
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||
|
||||
class SpecifySuperTypeFix(
|
||||
superExpression: KtSuperExpression,
|
||||
private val superTypes: List<String>
|
||||
) : KotlinQuickFixAction<KtSuperExpression>(superExpression) {
|
||||
|
||||
override fun getText() = "Specify supertype"
|
||||
|
||||
override fun getFamilyName() = text
|
||||
|
||||
override fun invoke(project: Project, editor: Editor?, file: KtFile) {
|
||||
if (editor == null) return
|
||||
val superExpression = element ?: return
|
||||
CommandProcessor.getInstance().runUndoTransparentAction {
|
||||
if (superTypes.size == 1) {
|
||||
superExpression.specifySuperType(superTypes.first())
|
||||
} else {
|
||||
JBPopupFactory
|
||||
.getInstance()
|
||||
.createListPopup(createListPopupStep(superExpression, superTypes))
|
||||
.showInBestPositionFor(editor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtSuperExpression.specifySuperType(superType: String) {
|
||||
project.executeWriteCommand("Specify supertype") {
|
||||
val label = this.labelQualifier?.text ?: ""
|
||||
replace(KtPsiFactory(this).createExpression("super<$superType>$label"))
|
||||
}
|
||||
}
|
||||
|
||||
private fun createListPopupStep(superExpression: KtSuperExpression, superTypes: List<String>): ListPopupStep<*> {
|
||||
return object : BaseListPopupStep<String>("Choose supertype", superTypes) {
|
||||
override fun isAutoSelectionEnabled() = false
|
||||
|
||||
override fun onChosen(selectedValue: String, finalChoice: Boolean): PopupStep<*>? {
|
||||
if (finalChoice) {
|
||||
superExpression.specifySuperType(selectedValue)
|
||||
}
|
||||
return PopupStep.FINAL_CHOICE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object : KotlinSingleIntentionActionFactory() {
|
||||
override fun createAction(diagnostic: Diagnostic): KotlinQuickFixAction<KtSuperExpression>? {
|
||||
val superExpression = diagnostic.psiElement as? KtSuperExpression ?: return null
|
||||
val qualifiedExpression = superExpression.getQualifiedExpressionForReceiver() ?: return null
|
||||
val selectorExpression = qualifiedExpression.selectorExpression ?: return null
|
||||
|
||||
val containingClassOrObject = superExpression.getStrictParentOfType<KtClassOrObject>() ?: return null
|
||||
val superTypeListEntries = containingClassOrObject.superTypeListEntries
|
||||
if (superTypeListEntries.isEmpty()) return null
|
||||
|
||||
val context = superExpression.analyze(BodyResolveMode.PARTIAL)
|
||||
val superTypes = superTypeListEntries.mapNotNull {
|
||||
val typeReference = it.typeReference ?: return@mapNotNull null
|
||||
val typeElement = it.typeReference?.typeElement ?: return@mapNotNull null
|
||||
val kotlinType = context[BindingContext.TYPE, typeReference] ?: return@mapNotNull null
|
||||
typeElement to kotlinType
|
||||
}
|
||||
if (superTypes.size != superTypeListEntries.size) return null
|
||||
|
||||
val psiFactory = KtPsiFactory(superExpression)
|
||||
val superTypesForSuperExpression = superTypes.mapNotNull { (typeElement, kotlinType) ->
|
||||
if (superTypes.any { it.second != kotlinType && it.second.isSubtypeOf(kotlinType) }) return@mapNotNull null
|
||||
val fqName = kotlinType.fqName ?: return@mapNotNull null
|
||||
val fqNameAsString = fqName.asString()
|
||||
val name = if (typeElement.text.startsWith(fqNameAsString)) fqNameAsString else fqName.shortName().asString()
|
||||
val newQualifiedExpression = psiFactory.createExpressionByPattern("super<$name>.$0", selectorExpression)
|
||||
val newContext = newQualifiedExpression.analyzeAsReplacement(qualifiedExpression, context)
|
||||
if (newQualifiedExpression.getResolvedCall(newContext)?.resultingDescriptor == null) return@mapNotNull null
|
||||
if (newContext.diagnostics.noSuppression().forElement(newQualifiedExpression).isNotEmpty()) return@mapNotNull null
|
||||
name
|
||||
}
|
||||
if (superTypesForSuperExpression.isEmpty()) return null
|
||||
|
||||
return SpecifySuperTypeFix(superExpression, superTypesForSuperExpression)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// "Specify supertype" "true"
|
||||
package a.b.c
|
||||
|
||||
interface X
|
||||
|
||||
open class Y {
|
||||
open fun foo() {}
|
||||
}
|
||||
|
||||
interface Z {
|
||||
fun foo() {}
|
||||
}
|
||||
|
||||
class Test : X, a.b.c.Y(), Z {
|
||||
override fun foo() {
|
||||
<caret>super.foo()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// "Specify supertype" "true"
|
||||
package a.b.c
|
||||
|
||||
interface X
|
||||
|
||||
open class Y {
|
||||
open fun foo() {}
|
||||
}
|
||||
|
||||
interface Z {
|
||||
fun foo() {}
|
||||
}
|
||||
|
||||
class Test : X, a.b.c.Y(), Z {
|
||||
override fun foo() {
|
||||
super<a.b.c.Y>.foo()
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// "Specify supertype" "true"
|
||||
package a.b.c
|
||||
|
||||
interface Z {
|
||||
fun foo() {}
|
||||
}
|
||||
|
||||
open class X {
|
||||
open fun foo() {}
|
||||
}
|
||||
|
||||
class Test : (@Suppress("foo") a.b.c.X)(), Z {
|
||||
override fun foo() {
|
||||
<caret>super.foo()
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// "Specify supertype" "true"
|
||||
package a.b.c
|
||||
|
||||
interface Z {
|
||||
fun foo() {}
|
||||
}
|
||||
|
||||
open class X {
|
||||
open fun foo() {}
|
||||
}
|
||||
|
||||
class Test : (@Suppress("foo") a.b.c.X)(), Z {
|
||||
override fun foo() {
|
||||
super<a.b.c.X>.foo()
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
// "Specify supertype" "true"
|
||||
package a.b.c
|
||||
|
||||
interface X {}
|
||||
|
||||
open class Y<T> {
|
||||
open fun foo() {}
|
||||
}
|
||||
|
||||
interface Z {
|
||||
fun foo() {}
|
||||
}
|
||||
|
||||
class Test : a.b.c.Y<Int>(), X, Z {
|
||||
override fun foo() {
|
||||
<caret>super.foo()
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
// "Specify supertype" "true"
|
||||
package a.b.c
|
||||
|
||||
interface X {}
|
||||
|
||||
open class Y<T> {
|
||||
open fun foo() {}
|
||||
}
|
||||
|
||||
interface Z {
|
||||
fun foo() {}
|
||||
}
|
||||
|
||||
class Test : a.b.c.Y<Int>(), X, Z {
|
||||
override fun foo() {
|
||||
super<a.b.c.Y>.foo()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// "Specify supertype" "true"
|
||||
package a.b.c
|
||||
|
||||
interface X
|
||||
|
||||
open class Y {
|
||||
open fun foo() {}
|
||||
}
|
||||
|
||||
interface Z {
|
||||
fun foo() {}
|
||||
}
|
||||
|
||||
class Test : X, Y(), Z {
|
||||
override fun foo() {
|
||||
<caret>super.foo()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// "Specify supertype" "true"
|
||||
package a.b.c
|
||||
|
||||
interface X
|
||||
|
||||
open class Y {
|
||||
open fun foo() {}
|
||||
}
|
||||
|
||||
interface Z {
|
||||
fun foo() {}
|
||||
}
|
||||
|
||||
class Test : X, Y(), Z {
|
||||
override fun foo() {
|
||||
super<Y>.foo()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// "Specify supertype" "true"
|
||||
interface X
|
||||
|
||||
open class Y {
|
||||
open val bar
|
||||
get() = 1
|
||||
}
|
||||
|
||||
interface Z {
|
||||
val bar
|
||||
get() = 2
|
||||
}
|
||||
|
||||
class Test : X, Z, Y() {
|
||||
override val bar: Int
|
||||
get() = <caret>super.bar
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// "Specify supertype" "true"
|
||||
interface X
|
||||
|
||||
open class Y {
|
||||
open val bar
|
||||
get() = 1
|
||||
}
|
||||
|
||||
interface Z {
|
||||
val bar
|
||||
get() = 2
|
||||
}
|
||||
|
||||
class Test : X, Z, Y() {
|
||||
override val bar: Int
|
||||
get() = super<Z>.bar
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// "Specify supertype" "true"
|
||||
interface X
|
||||
|
||||
open class Y {
|
||||
open fun foo() {}
|
||||
}
|
||||
|
||||
interface Z {
|
||||
fun foo() {}
|
||||
}
|
||||
|
||||
class Test : X, Y(), Z {
|
||||
override fun foo() {}
|
||||
|
||||
inner class Boo : Y(), Z {
|
||||
override fun foo() {
|
||||
<caret>super@Test.foo()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// "Specify supertype" "true"
|
||||
interface X
|
||||
|
||||
open class Y {
|
||||
open fun foo() {}
|
||||
}
|
||||
|
||||
interface Z {
|
||||
fun foo() {}
|
||||
}
|
||||
|
||||
class Test : X, Y(), Z {
|
||||
override fun foo() {}
|
||||
|
||||
inner class Boo : Y(), Z {
|
||||
override fun foo() {
|
||||
super<Y>@Test.foo()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// "Specify supertype" "true"
|
||||
interface Z {
|
||||
fun foo() {}
|
||||
}
|
||||
|
||||
open class X {
|
||||
open fun foo() {}
|
||||
}
|
||||
|
||||
class Test : (@Suppress("foo") X)(), Z {
|
||||
override fun foo() {
|
||||
<caret>super.foo()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// "Specify supertype" "true"
|
||||
interface Z {
|
||||
fun foo() {}
|
||||
}
|
||||
|
||||
open class X {
|
||||
open fun foo() {}
|
||||
}
|
||||
|
||||
class Test : (@Suppress("foo") X)(), Z {
|
||||
override fun foo() {
|
||||
super<X>.foo()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// "Specify supertype" "true"
|
||||
interface X {}
|
||||
|
||||
open class Y<T> {
|
||||
open fun foo() {}
|
||||
}
|
||||
|
||||
interface Z {
|
||||
fun foo() {}
|
||||
}
|
||||
|
||||
class Test : Y<Int>(), X, Z {
|
||||
override fun foo() {
|
||||
<caret>super.foo()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// "Specify supertype" "true"
|
||||
interface X {}
|
||||
|
||||
open class Y<T> {
|
||||
open fun foo() {}
|
||||
}
|
||||
|
||||
interface Z {
|
||||
fun foo() {}
|
||||
}
|
||||
|
||||
class Test : Y<Int>(), X, Z {
|
||||
override fun foo() {
|
||||
super<Y>.foo()
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
// "Specify supertype" "true"
|
||||
interface X {
|
||||
fun foo() {}
|
||||
}
|
||||
|
||||
open class Y: X {
|
||||
override fun foo() {}
|
||||
}
|
||||
|
||||
interface Z {
|
||||
fun foo() {}
|
||||
}
|
||||
|
||||
class Test : X, Y(), Z {
|
||||
override fun foo() {
|
||||
<caret>super.foo()
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
// "Specify supertype" "true"
|
||||
interface X {
|
||||
fun foo() {}
|
||||
}
|
||||
|
||||
open class Y: X {
|
||||
override fun foo() {}
|
||||
}
|
||||
|
||||
interface Z {
|
||||
fun foo() {}
|
||||
}
|
||||
|
||||
class Test : X, Y(), Z {
|
||||
override fun foo() {
|
||||
super<Y>.foo()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// "Specify supertype" "true"
|
||||
// DISABLE-ERRORS
|
||||
interface Z {
|
||||
fun foo(): CharSequence = ""
|
||||
}
|
||||
|
||||
open class Y {
|
||||
override fun foo(): String = ""
|
||||
}
|
||||
|
||||
class Test : Z, Y() {
|
||||
override fun foo(): String {
|
||||
return <caret>super.foo()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// "Specify supertype" "true"
|
||||
// DISABLE-ERRORS
|
||||
interface Z {
|
||||
fun foo(): CharSequence = ""
|
||||
}
|
||||
|
||||
open class Y {
|
||||
override fun foo(): String = ""
|
||||
}
|
||||
|
||||
class Test : Z, Y() {
|
||||
override fun foo(): String {
|
||||
return super<Y>.foo()
|
||||
}
|
||||
}
|
||||
+13
@@ -4223,6 +4223,19 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/quickfix/specifySuperType")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class SpecifySuperType extends AbstractQuickFixMultiFileTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTestWithExtraFile, this, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInSpecifySuperType() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/quickfix/specifySuperType"), Pattern.compile("^(\\w+)\\.((before\\.Main\\.\\w+)|(test))$"), null, true);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/quickfix/specifyVisibilityInExplicitApiMode")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
@@ -12068,6 +12068,69 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/quickfix/specifySuperType")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class SpecifySuperType extends AbstractQuickFixTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInSpecifySuperType() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/quickfix/specifySuperType"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), null, true);
|
||||
}
|
||||
|
||||
@TestMetadata("fullyQualifiedSuperType.kt")
|
||||
public void testFullyQualifiedSuperType() throws Exception {
|
||||
runTest("idea/testData/quickfix/specifySuperType/fullyQualifiedSuperType.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("fullyQualifiedSuperTypeHasAnnotations.kt")
|
||||
public void testFullyQualifiedSuperTypeHasAnnotations() throws Exception {
|
||||
runTest("idea/testData/quickfix/specifySuperType/fullyQualifiedSuperTypeHasAnnotations.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("fullyQualifiedSuperTypeHasTypeArguments.kt")
|
||||
public void testFullyQualifiedSuperTypeHasTypeArguments() throws Exception {
|
||||
runTest("idea/testData/quickfix/specifySuperType/fullyQualifiedSuperTypeHasTypeArguments.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("function.kt")
|
||||
public void testFunction() throws Exception {
|
||||
runTest("idea/testData/quickfix/specifySuperType/function.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("property.kt")
|
||||
public void testProperty() throws Exception {
|
||||
runTest("idea/testData/quickfix/specifySuperType/property.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("superExpressionHasLabel.kt")
|
||||
public void testSuperExpressionHasLabel() throws Exception {
|
||||
runTest("idea/testData/quickfix/specifySuperType/superExpressionHasLabel.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("superTypeHasAnnotations.kt")
|
||||
public void testSuperTypeHasAnnotations() throws Exception {
|
||||
runTest("idea/testData/quickfix/specifySuperType/superTypeHasAnnotations.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("superTypeHasTypeArguments.kt")
|
||||
public void testSuperTypeHasTypeArguments() throws Exception {
|
||||
runTest("idea/testData/quickfix/specifySuperType/superTypeHasTypeArguments.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("superTypeIsExtendedByOtherSuperType.kt")
|
||||
public void testSuperTypeIsExtendedByOtherSuperType() throws Exception {
|
||||
runTest("idea/testData/quickfix/specifySuperType/superTypeIsExtendedByOtherSuperType.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("typeMismatch.kt")
|
||||
public void testTypeMismatch() throws Exception {
|
||||
runTest("idea/testData/quickfix/specifySuperType/typeMismatch.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/quickfix/specifyVisibilityInExplicitApiMode")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Reference in New Issue
Block a user