FIR IDE: register quickfix for the following

1. NON_ABSTRACT_FUNCTION_WITH_NO_BODY
  2. ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS
  3. ABSTRACT_FUNCTION_IN_NON_ABSTRACT_CLASS
This commit is contained in:
Tianyu Geng
2021-02-10 17:26:43 -08:00
committed by Ilya Kirillov
parent 88c43e7f7b
commit 56854a8b1a
33 changed files with 251 additions and 28 deletions
@@ -1114,6 +1114,7 @@ fun main(args: Array<String>) {
testClass<AbstractHighLevelQuickFixTest> { testClass<AbstractHighLevelQuickFixTest> {
val pattern = "^([\\w\\-_]+)\\.kt$" val pattern = "^([\\w\\-_]+)\\.kt$"
model("quickfix/abstract", pattern = pattern, filenameStartsLowerCase = true)
model("quickfix/lateinit", pattern = pattern, filenameStartsLowerCase = true) model("quickfix/lateinit", pattern = pattern, filenameStartsLowerCase = true)
model("quickfix/modifiers", pattern = pattern, filenameStartsLowerCase = true, recursive = false) model("quickfix/modifiers", pattern = pattern, filenameStartsLowerCase = true, recursive = false)
model("quickfix/override/typeMismatchOnOverride", pattern = pattern, filenameStartsLowerCase = true, recursive = false) model("quickfix/override/typeMismatchOnOverride", pattern = pattern, filenameStartsLowerCase = true, recursive = false)
@@ -10,7 +10,6 @@ import org.jetbrains.kotlin.idea.fir.api.fixes.KtQuickFixesList
import org.jetbrains.kotlin.idea.fir.api.fixes.KtQuickFixesListBuilder import org.jetbrains.kotlin.idea.fir.api.fixes.KtQuickFixesListBuilder
import org.jetbrains.kotlin.idea.frontend.api.fir.diagnostics.KtFirDiagnostic import org.jetbrains.kotlin.idea.frontend.api.fir.diagnostics.KtFirDiagnostic
import org.jetbrains.kotlin.idea.quickfix.fixes.ChangeTypeQuickFix import org.jetbrains.kotlin.idea.quickfix.fixes.ChangeTypeQuickFix
import org.jetbrains.kotlin.lexer.KtTokens
class MainKtQuickFixRegistrar : KtQuickFixRegistrar() { class MainKtQuickFixRegistrar : KtQuickFixRegistrar() {
private val modifiers = KtQuickFixesListBuilder.registerPsiQuickFix { private val modifiers = KtQuickFixesListBuilder.registerPsiQuickFix {
@@ -19,12 +18,17 @@ class MainKtQuickFixRegistrar : KtQuickFixRegistrar() {
registerPsiQuickFixes(KtFirDiagnostic.RepeatedModifier::class, RemoveModifierFix.removeNonRedundantModifier) registerPsiQuickFixes(KtFirDiagnostic.RepeatedModifier::class, RemoveModifierFix.removeNonRedundantModifier)
registerPsiQuickFixes(KtFirDiagnostic.DeprecatedModifierPair::class, RemoveModifierFix.removeRedundantModifier) registerPsiQuickFixes(KtFirDiagnostic.DeprecatedModifierPair::class, RemoveModifierFix.removeRedundantModifier)
registerPsiQuickFixes(KtFirDiagnostic.TypeParametersInEnum::class, RemoveModifierFix.removeRedundantModifier) registerPsiQuickFixes(KtFirDiagnostic.TypeParametersInEnum::class, RemoveModifierFix.removeRedundantModifier)
registerPsiQuickFixes(KtFirDiagnostic.RedundantOpenInInterface::class, RemoveModifierFix.removeRedundantOpenModifier)
registerPsiQuickFixes(KtFirDiagnostic.NonAbstractFunctionWithNoBody::class, AddFunctionBodyFix, AddModifierFix.addAbstractModifier)
registerPsiQuickFixes( registerPsiQuickFixes(
KtFirDiagnostic.RedundantOpenInInterface::class, KtFirDiagnostic.AbstractPropertyInNonAbstractClass::class,
RemoveModifierFix.createRemoveModifierFromListOwnerFactoryByModifierListOwner( AddModifierFix.addAbstractToContainingClass,
modifier = KtTokens.OPEN_KEYWORD, RemoveModifierFix.removeAbstractModifier
isRedundant = true )
) registerPsiQuickFixes(
KtFirDiagnostic.AbstractFunctionInNonAbstractClass::class,
AddModifierFix.addAbstractToContainingClass,
RemoveModifierFix.removeAbstractModifier
) )
} }
@@ -19,6 +19,184 @@ import java.util.regex.Pattern;
@SuppressWarnings("all") @SuppressWarnings("all")
@RunWith(JUnit3RunnerWithInners.class) @RunWith(JUnit3RunnerWithInners.class)
public class HighLevelQuickFixTestGenerated extends AbstractHighLevelQuickFixTest { public class HighLevelQuickFixTestGenerated extends AbstractHighLevelQuickFixTest {
@TestMetadata("idea/testData/quickfix/abstract")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Abstract extends AbstractHighLevelQuickFixTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@TestMetadata("abstractFunctionInNonAbstractClass.kt")
public void testAbstractFunctionInNonAbstractClass() throws Exception {
runTest("idea/testData/quickfix/abstract/abstractFunctionInNonAbstractClass.kt");
}
@TestMetadata("abstractFunctionWithBody.kt")
public void testAbstractFunctionWithBody() throws Exception {
runTest("idea/testData/quickfix/abstract/abstractFunctionWithBody.kt");
}
@TestMetadata("abstractFunctionWithBody2.kt")
public void testAbstractFunctionWithBody2() throws Exception {
runTest("idea/testData/quickfix/abstract/abstractFunctionWithBody2.kt");
}
@TestMetadata("abstractFunctionWithBody3.kt")
public void testAbstractFunctionWithBody3() throws Exception {
runTest("idea/testData/quickfix/abstract/abstractFunctionWithBody3.kt");
}
@TestMetadata("abstractFunctionWithBodyWithComments.kt")
public void testAbstractFunctionWithBodyWithComments() throws Exception {
runTest("idea/testData/quickfix/abstract/abstractFunctionWithBodyWithComments.kt");
}
@TestMetadata("abstractFunctionWithBodyWithComments2.kt")
public void testAbstractFunctionWithBodyWithComments2() throws Exception {
runTest("idea/testData/quickfix/abstract/abstractFunctionWithBodyWithComments2.kt");
}
@TestMetadata("abstractPropertyInCompanionObject.kt")
public void testAbstractPropertyInCompanionObject() throws Exception {
runTest("idea/testData/quickfix/abstract/abstractPropertyInCompanionObject.kt");
}
@TestMetadata("abstractPropertyInNonAbstractClass1.kt")
public void testAbstractPropertyInNonAbstractClass1() throws Exception {
runTest("idea/testData/quickfix/abstract/abstractPropertyInNonAbstractClass1.kt");
}
@TestMetadata("abstractPropertyInNonAbstractClass2.kt")
public void testAbstractPropertyInNonAbstractClass2() throws Exception {
runTest("idea/testData/quickfix/abstract/abstractPropertyInNonAbstractClass2.kt");
}
@TestMetadata("abstractPropertyInNonAbstractClass3.kt")
public void testAbstractPropertyInNonAbstractClass3() throws Exception {
runTest("idea/testData/quickfix/abstract/abstractPropertyInNonAbstractClass3.kt");
}
@TestMetadata("abstractPropertyInPrimaryConstructorParameters.kt")
public void testAbstractPropertyInPrimaryConstructorParameters() throws Exception {
runTest("idea/testData/quickfix/abstract/abstractPropertyInPrimaryConstructorParameters.kt");
}
@TestMetadata("abstractPropertyNotInClass.kt")
public void testAbstractPropertyNotInClass() throws Exception {
runTest("idea/testData/quickfix/abstract/abstractPropertyNotInClass.kt");
}
@TestMetadata("abstractPropertyWIthInitializer2.kt")
public void testAbstractPropertyWIthInitializer2() throws Exception {
runTest("idea/testData/quickfix/abstract/abstractPropertyWIthInitializer2.kt");
}
@TestMetadata("abstractPropertyWIthInitializer3.kt")
public void testAbstractPropertyWIthInitializer3() throws Exception {
runTest("idea/testData/quickfix/abstract/abstractPropertyWIthInitializer3.kt");
}
@TestMetadata("abstractPropertyWithGetter1.kt")
public void testAbstractPropertyWithGetter1() throws Exception {
runTest("idea/testData/quickfix/abstract/abstractPropertyWithGetter1.kt");
}
@TestMetadata("abstractPropertyWithGetter2.kt")
public void testAbstractPropertyWithGetter2() throws Exception {
runTest("idea/testData/quickfix/abstract/abstractPropertyWithGetter2.kt");
}
@TestMetadata("abstractPropertyWithInitializer1.kt")
public void testAbstractPropertyWithInitializer1() throws Exception {
runTest("idea/testData/quickfix/abstract/abstractPropertyWithInitializer1.kt");
}
@TestMetadata("abstractPropertyWithSetter.kt")
public void testAbstractPropertyWithSetter() throws Exception {
runTest("idea/testData/quickfix/abstract/abstractPropertyWithSetter.kt");
}
public void testAllFilesPresentInAbstract() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/quickfix/abstract"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), null, true);
}
@TestMetadata("makeEnumEntryAbstract.kt")
public void testMakeEnumEntryAbstract() throws Exception {
runTest("idea/testData/quickfix/abstract/makeEnumEntryAbstract.kt");
}
@TestMetadata("makeInlineClassAbstract.kt")
public void testMakeInlineClassAbstract() throws Exception {
runTest("idea/testData/quickfix/abstract/makeInlineClassAbstract.kt");
}
@TestMetadata("makeObjectMemberAbstract.kt")
public void testMakeObjectMemberAbstract() throws Exception {
runTest("idea/testData/quickfix/abstract/makeObjectMemberAbstract.kt");
}
@TestMetadata("makeTopLevelAbstract.kt")
public void testMakeTopLevelAbstract() throws Exception {
runTest("idea/testData/quickfix/abstract/makeTopLevelAbstract.kt");
}
@TestMetadata("manyImpl.kt")
public void testManyImpl() throws Exception {
runTest("idea/testData/quickfix/abstract/manyImpl.kt");
}
@TestMetadata("mustBeInitializedOrBeAbstract.kt")
public void testMustBeInitializedOrBeAbstract() throws Exception {
runTest("idea/testData/quickfix/abstract/mustBeInitializedOrBeAbstract.kt");
}
@TestMetadata("mustBeInitializedOrBeAbstractInFinalClass.kt")
public void testMustBeInitializedOrBeAbstractInFinalClass() throws Exception {
runTest("idea/testData/quickfix/abstract/mustBeInitializedOrBeAbstractInFinalClass.kt");
}
@TestMetadata("mustBeInitializedOrBeAbstractInOpenClass.kt")
public void testMustBeInitializedOrBeAbstractInOpenClass() throws Exception {
runTest("idea/testData/quickfix/abstract/mustBeInitializedOrBeAbstractInOpenClass.kt");
}
@TestMetadata("mustBeInitializedOrBeAbstractInSealedClass.kt")
public void testMustBeInitializedOrBeAbstractInSealedClass() throws Exception {
runTest("idea/testData/quickfix/abstract/mustBeInitializedOrBeAbstractInSealedClass.kt");
}
@TestMetadata("nonAbstractFunctionWithNoBody.kt")
public void testNonAbstractFunctionWithNoBody() throws Exception {
runTest("idea/testData/quickfix/abstract/nonAbstractFunctionWithNoBody.kt");
}
@TestMetadata("nonMemberAbstractFunction.kt")
public void testNonMemberAbstractFunction() throws Exception {
runTest("idea/testData/quickfix/abstract/nonMemberAbstractFunction.kt");
}
@TestMetadata("nonMemberFunctionNoBody.kt")
public void testNonMemberFunctionNoBody() throws Exception {
runTest("idea/testData/quickfix/abstract/nonMemberFunctionNoBody.kt");
}
@TestMetadata("notImplementedMember.kt")
public void testNotImplementedMember() throws Exception {
runTest("idea/testData/quickfix/abstract/notImplementedMember.kt");
}
@TestMetadata("notImplementedMemberFromAbstractClass.kt")
public void testNotImplementedMemberFromAbstractClass() throws Exception {
runTest("idea/testData/quickfix/abstract/notImplementedMemberFromAbstractClass.kt");
}
@TestMetadata("replaceOpen.kt")
public void testReplaceOpen() throws Exception {
runTest("idea/testData/quickfix/abstract/replaceOpen.kt");
}
}
@TestMetadata("idea/testData/quickfix/lateinit") @TestMetadata("idea/testData/quickfix/lateinit")
@TestDataPath("$PROJECT_ROOT") @TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class) @RunWith(JUnit3RunnerWithInners.class)
@@ -5,14 +5,13 @@
package org.jetbrains.kotlin.idea.quickfix package org.jetbrains.kotlin.idea.quickfix
import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.idea.KotlinBundle import org.jetbrains.kotlin.idea.KotlinBundle
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtFunction import org.jetbrains.kotlin.psi.KtFunction
import org.jetbrains.kotlin.psi.KtPsiFactory import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
class AddFunctionBodyFix(element: KtFunction) : KotlinPsiOnlyQuickFixAction<KtFunction>(element) { class AddFunctionBodyFix(element: KtFunction) : KotlinPsiOnlyQuickFixAction<KtFunction>(element) {
override fun getFamilyName() = KotlinBundle.message("fix.add.function.body") override fun getFamilyName() = KotlinBundle.message("fix.add.function.body")
@@ -30,9 +29,7 @@ class AddFunctionBodyFix(element: KtFunction) : KotlinPsiOnlyQuickFixAction<KtFu
} }
} }
companion object : KotlinSingleIntentionActionFactory() { companion object : QuickFixesPsiBasedFactory<KtFunction>(KtFunction::class, PsiElementSuitabilityCheckers.ALWAYS_SUITABLE) {
public override fun createAction(diagnostic: Diagnostic): AddFunctionBodyFix? { override fun doCreateQuickFix(psiElement: KtFunction): List<IntentionAction> = listOfNotNull(AddFunctionBodyFix(psiElement))
return diagnostic.psiElement.getNonStrictParentOfType<KtFunction>()?.let(::AddFunctionBodyFix)
}
} }
} }
@@ -106,9 +106,14 @@ open class AddModifierFix(
} }
companion object : Factory<AddModifierFix> { companion object : Factory<AddModifierFix> {
private val modalityModifiers = setOf(KtTokens.ABSTRACT_KEYWORD, KtTokens.OPEN_KEYWORD, KtTokens.FINAL_KEYWORD) val addAbstractModifier: QuickFixesPsiBasedFactory<PsiElement> = AddModifierFix.createFactory(KtTokens.ABSTRACT_KEYWORD)
val addAbstractToContainingClass: QuickFixesPsiBasedFactory<PsiElement> =
AddModifierFix.createFactory(KtTokens.ABSTRACT_KEYWORD, KtClassOrObject::class.java)
private val modalityModifiers: Set<KtModifierKeywordToken> =
setOf(KtTokens.ABSTRACT_KEYWORD, KtTokens.OPEN_KEYWORD, KtTokens.FINAL_KEYWORD)
override fun createModifierFix(element: KtModifierListOwner, modifier: KtModifierKeywordToken): AddModifierFix = override fun createModifierFix(element: KtModifierListOwner, modifier: KtModifierKeywordToken): AddModifierFix =
AddModifierFix(element, modifier) AddModifierFix(element, modifier)
} }
} }
@@ -51,8 +51,15 @@ class RemoveModifierFix(
} }
companion object { companion object {
val removeRedundantModifier = createRemoveModifierFactory(isRedundant = true) val removeRedundantModifier: QuickFixesPsiBasedFactory<PsiElement> = createRemoveModifierFactory(isRedundant = true)
val removeNonRedundantModifier = createRemoveModifierFactory(isRedundant = false) val removeNonRedundantModifier: QuickFixesPsiBasedFactory<PsiElement> = createRemoveModifierFactory(isRedundant = false)
val removeAbstractModifier: QuickFixesPsiBasedFactory<PsiElement> =
createRemoveModifierFromListOwnerPsiBasedFactory(KtTokens.ABSTRACT_KEYWORD)
val removeRedundantOpenModifier: QuickFixesPsiBasedFactory<KtModifierListOwner> =
createRemoveModifierFromListOwnerFactoryByModifierListOwner(
modifier = KtTokens.OPEN_KEYWORD,
isRedundant = true
)
@Deprecated( @Deprecated(
"For binary compatibility", "For binary compatibility",
@@ -1,4 +1,5 @@
// "Make 'foo' not abstract" "true" // "Make 'foo' not abstract" "true"
class A() { class A() {
<caret>abstract fun foo() {} <caret>abstract fun foo() {}
} }
/* FIR_COMPARISON */
@@ -1,4 +1,5 @@
// "Make 'foo' not abstract" "true" // "Make 'foo' not abstract" "true"
class A() { class A() {
fun foo() {} fun foo() {}
} }
/* FIR_COMPARISON */
@@ -7,4 +7,5 @@ class Owner {
companion object { companion object {
<caret>abstract val x: Int <caret>abstract val x: Int
} }
} }
/* FIR_COMPARISON */
@@ -2,3 +2,4 @@
class A() { class A() {
<caret>abstract var i : Int <caret>abstract var i : Int
} }
/* FIR_COMPARISON */
@@ -2,3 +2,4 @@
abstract class A() { abstract class A() {
<caret>abstract var i : Int <caret>abstract var i : Int
} }
/* FIR_COMPARISON */
@@ -2,3 +2,4 @@
class A() { class A() {
<caret>abstract var i : Int = 0 <caret>abstract var i : Int = 0
} }
/* FIR_COMPARISON */
@@ -2,3 +2,4 @@
class A() { class A() {
var i : Int = 0 var i : Int = 0
} }
/* FIR_COMPARISON */
@@ -2,3 +2,4 @@
public class A() { public class A() {
<caret>abstract var i : Int <caret>abstract var i : Int
} }
/* FIR_COMPARISON */
@@ -2,3 +2,4 @@
public abstract class A() { public abstract class A() {
<caret>abstract var i : Int <caret>abstract var i : Int
} }
/* FIR_COMPARISON */
@@ -2,4 +2,5 @@
class B { class B {
<caret>abstract val i: Int = 0 <caret>abstract val i: Int = 0
get() = field get() = field
} }
/* FIR_COMPARISON */
@@ -2,4 +2,5 @@
class B { class B {
val i: Int = 0 val i: Int = 0
get() = field get() = field
} }
/* FIR_COMPARISON */
@@ -2,3 +2,4 @@
class A { class A {
<caret>abstract var i = 0 <caret>abstract var i = 0
} }
/* FIR_COMPARISON */
@@ -2,3 +2,4 @@
class A { class A {
<caret>var i = 0 <caret>var i = 0
} }
/* FIR_COMPARISON */
@@ -3,3 +3,4 @@ class B {
abstract<caret> var j: Int = 0 abstract<caret> var j: Int = 0
set(v: Int) {} set(v: Int) {}
} }
/* FIR_COMPARISON */
@@ -3,3 +3,4 @@ class B {
var j: Int = 0 var j: Int = 0
set(v: Int) {} set(v: Int) {}
} }
/* FIR_COMPARISON */
+2 -1
View File
@@ -6,4 +6,5 @@ enum class E {
<caret>A; <caret>A;
abstract fun foo() abstract fun foo()
} }
/* FIR_COMPARISON */
+2 -1
View File
@@ -9,4 +9,5 @@ interface I {
fun foo(): String fun foo(): String
} }
inline class A<caret> : I inline class A<caret> : I
/* FIR_COMPARISON */
@@ -7,4 +7,5 @@
object O { object O {
<caret>fun foo() <caret>fun foo()
} }
/* FIR_COMPARISON */
+2 -1
View File
@@ -5,4 +5,5 @@
// ACTION: Make private // ACTION: Make private
// ERROR: Function 'foo' must have a body // ERROR: Function 'foo' must have a body
<caret>fun foo() <caret>fun foo()
/* FIR_COMPARISON */
+2 -1
View File
@@ -18,4 +18,5 @@ object Impl : D, E {
override fun foo() {} override fun foo() {}
} }
<caret>class X : D by Impl, E by Impl {} <caret>class X : D by Impl, E by Impl {}
/* FIR_COMPARISON */
@@ -2,3 +2,4 @@
sealed class A() { sealed class A() {
fun <caret>i() : Int fun <caret>i() : Int
} }
/* FIR_COMPARISON */
@@ -2,3 +2,4 @@
sealed class A() { sealed class A() {
abstract fun <caret>i() : Int abstract fun <caret>i() : Int
} }
/* FIR_COMPARISON */
@@ -2,3 +2,4 @@
class A() { class A() {
fun <caret>foo() fun <caret>foo()
} }
/* FIR_COMPARISON */
@@ -2,3 +2,4 @@
class A() { class A() {
fun <caret>foo() {} fun <caret>foo() {}
} }
/* FIR_COMPARISON */
+1
View File
@@ -2,3 +2,4 @@
abstract class B() { abstract class B() {
open fun <caret>foo() open fun <caret>foo()
} }
/* FIR_COMPARISON */
+1
View File
@@ -2,3 +2,4 @@
abstract class B() { abstract class B() {
abstract fun <caret>foo() abstract fun <caret>foo()
} }
/* FIR_COMPARISON */
@@ -29,9 +29,9 @@ public class KaptToolIntegrationTestGenerated extends AbstractKaptToolIntegratio
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/kapt3/kapt3-cli/testData/integration"), Pattern.compile("^([^\\.]+)$"), null, false); KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/kapt3/kapt3-cli/testData/integration"), Pattern.compile("^([^\\.]+)$"), null, false);
} }
@TestMetadata("defaultPackage") @TestMetadata("argfile")
public void testArgfile() throws Exception { public void testArgfile() throws Exception {
runTest("plugins/kapt3/kapt3-cli/testData/integration/defaultPackage/"); runTest("plugins/kapt3/kapt3-cli/testData/integration/argfile/");
} }
@TestMetadata("correctErrorTypesOff") @TestMetadata("correctErrorTypesOff")
@@ -44,6 +44,11 @@ public class KaptToolIntegrationTestGenerated extends AbstractKaptToolIntegratio
runTest("plugins/kapt3/kapt3-cli/testData/integration/correctErrorTypesOn/"); runTest("plugins/kapt3/kapt3-cli/testData/integration/correctErrorTypesOn/");
} }
@TestMetadata("defaultPackage")
public void testDefaultPackage() throws Exception {
runTest("plugins/kapt3/kapt3-cli/testData/integration/defaultPackage/");
}
@TestMetadata("kotlinFileGeneration") @TestMetadata("kotlinFileGeneration")
public void testKotlinFileGeneration() throws Exception { public void testKotlinFileGeneration() throws Exception {
runTest("plugins/kapt3/kapt3-cli/testData/integration/kotlinFileGeneration/"); runTest("plugins/kapt3/kapt3-cli/testData/integration/kotlinFileGeneration/");