FIR IDE: add intention to add custom accessor to uninitliazed property

This commit is contained in:
Tianyu Geng
2021-03-28 20:32:28 -07:00
committed by Ilya Kirillov
parent 78f450a6ef
commit 4d505f4393
16 changed files with 112 additions and 14 deletions
@@ -1134,6 +1134,7 @@ fun main(args: Array<String>) {
model("quickfix/replaceWithSafeCall", pattern = pattern, filenameStartsLowerCase = true)
model("quickfix/variables/changeMutability", pattern = pattern, filenameStartsLowerCase = true)
model("quickfix/addInitializer", pattern = pattern, filenameStartsLowerCase = true)
model("quickfix/addPropertyAccessors", pattern = pattern, filenameStartsLowerCase = true)
}
testClass<AbstractHLInspectionTest> {
@@ -0,0 +1,25 @@
/*
* Copyright 2010-2021 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.intentions.fir
import com.intellij.codeInsight.intention.PriorityAction
import com.intellij.openapi.util.TextRange
import org.jetbrains.kotlin.idea.intentions.AbstractAddAccessorsIntention
import org.jetbrains.kotlin.psi.KtProperty
class AddAccessorsIntention(
addGetter: Boolean, addSetter: Boolean,
private val priority: PriorityAction.Priority
) :
AbstractAddAccessorsIntention(addGetter, addSetter), PriorityAction {
// FE1.0 logic in org.jetbrains.kotlin.idea.intentions.AddAccessorsIntention has additional defensive checks to ensure the
// intention only shows up if the property in question indeed needs initialization or accessors. Those checks are unnecessary
// since this factory is coupled with MUST_BE_INITIALIZED_OR_BE_ABSTRACT diagnostic, which has already checked the applicability
// of this intention.
override fun applicabilityRange(element: KtProperty): TextRange? = element.textRange
override fun getPriority(): PriorityAction.Priority = priority
}
@@ -9,10 +9,8 @@ import org.jetbrains.kotlin.idea.fir.api.fixes.KtQuickFixRegistrar
import org.jetbrains.kotlin.idea.fir.api.fixes.KtQuickFixesList
import org.jetbrains.kotlin.idea.fir.api.fixes.KtQuickFixesListBuilder
import org.jetbrains.kotlin.idea.frontend.api.fir.diagnostics.KtFirDiagnostic
import org.jetbrains.kotlin.idea.quickfix.fixes.AddLateInitFactory
import org.jetbrains.kotlin.idea.quickfix.fixes.ChangeTypeQuickFix
import org.jetbrains.kotlin.idea.quickfix.fixes.*
import org.jetbrains.kotlin.idea.quickfix.fixes.InitializePropertyQuickFixFactory
import org.jetbrains.kotlin.idea.quickfix.fixes.ReplaceCallFixFactories
class MainKtQuickFixRegistrar : KtQuickFixRegistrar() {
private val modifiers = KtQuickFixesListBuilder.registerPsiQuickFix {
@@ -65,6 +63,7 @@ class MainKtQuickFixRegistrar : KtQuickFixRegistrar() {
)
registerApplicator(InitializePropertyQuickFixFactory.initializePropertyFactory)
registerApplicator(AddLateInitFactory.addLateInitFactory)
registerApplicator(AddAccessorsFactories.addAccessorsToUninitializedProperty)
}
private val overrides = KtQuickFixesListBuilder.registerPsiQuickFix {
@@ -0,0 +1,29 @@
/*
* Copyright 2010-2021 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.fixes
import com.intellij.codeInsight.intention.PriorityAction
import org.jetbrains.kotlin.idea.fir.api.fixes.diagnosticFixFactory
import org.jetbrains.kotlin.idea.frontend.api.fir.diagnostics.KtFirDiagnostic
import org.jetbrains.kotlin.idea.intentions.fir.AddAccessorsIntention
import org.jetbrains.kotlin.psi.KtProperty
object AddAccessorsFactories {
val addAccessorsToUninitializedProperty = diagnosticFixFactory<KtFirDiagnostic.MustBeInitializedOrBeAbstract> { diagnostic ->
val property: KtProperty = diagnostic.psi
val addGetter = property.getter == null
val addSetter = property.isVar && property.setter == null
if (!addGetter && !addSetter) return@diagnosticFixFactory emptyList()
listOf(
AddAccessorsIntention(
addGetter,
addSetter,
if (addGetter && addSetter) PriorityAction.Priority.LOW else PriorityAction.Priority.NORMAL
)
)
}
}
@@ -13,8 +13,6 @@ import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtProperty
object AddLateInitFactory {
@OptIn(ExperimentalStdlibApi::class)
val addLateInitFactory = diagnosticFixFactory<KtFirDiagnostic.MustBeInitializedOrBeAbstract> { diagnostic ->
val property: KtProperty = diagnostic.psi
if (!property.isVar) return@diagnosticFixFactory emptyList()
@@ -1230,4 +1230,37 @@ public class HighLevelQuickFixTestGenerated extends AbstractHighLevelQuickFixTes
runTest("idea/testData/quickfix/addInitializer/topLevelPropertyWithGetter.kt");
}
}
@TestMetadata("idea/testData/quickfix/addPropertyAccessors")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class AddPropertyAccessors extends AbstractHighLevelQuickFixTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInAddPropertyAccessors() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/quickfix/addPropertyAccessors"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), null, true);
}
@TestMetadata("val.kt")
public void testVal() throws Exception {
runTest("idea/testData/quickfix/addPropertyAccessors/val.kt");
}
@TestMetadata("var.kt")
public void testVar() throws Exception {
runTest("idea/testData/quickfix/addPropertyAccessors/var.kt");
}
@TestMetadata("varHasGetter.kt")
public void testVarHasGetter() throws Exception {
runTest("idea/testData/quickfix/addPropertyAccessors/varHasGetter.kt");
}
@TestMetadata("varHasSetter.kt")
public void testVarHasSetter() throws Exception {
runTest("idea/testData/quickfix/addPropertyAccessors/varHasSetter.kt");
}
}
}
@@ -21,6 +21,11 @@ val KtType.isChar: Boolean get() = isClassTypeWithClassId(DefaultTypeClassIds.CH
val KtType.isBoolean: Boolean get() = isClassTypeWithClassId(DefaultTypeClassIds.BOOLEAN)
val KtType.isString: Boolean get() = isClassTypeWithClassId(DefaultTypeClassIds.STRING)
val KtType.isUInt: Boolean get() = isClassTypeWithClassId(StandardNames.FqNames.uInt)
val KtType.isULong: Boolean get() = isClassTypeWithClassId(StandardNames.FqNames.uLong)
val KtType.isUShort: Boolean get() = isClassTypeWithClassId(StandardNames.FqNames.uShort)
val KtType.isUByte: Boolean get() = isClassTypeWithClassId(StandardNames.FqNames.uByte)
fun KtType.isClassTypeWithClassId(classId: ClassId): Boolean {
if (this !is KtClassType) return false
@@ -57,5 +62,9 @@ val KtType.defaultInitializer: String?
isBoolean -> "false"
isUnit -> "Unit"
isString -> "\"\""
isUInt -> "0.toUInt()"
isULong -> "0.toULong()"
isUShort -> "0.toUShort()"
isUByte -> "0.toUByte()"
else -> null
}
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2021 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.
*/
+1 -1
View File
@@ -2,4 +2,4 @@
// WITH_RUNTIME
class Test {
val x: Int<caret>
}
}
+1 -1
View File
@@ -5,4 +5,4 @@ class Test {
get() {
<caret>TODO()
}
}
}
+1 -1
View File
@@ -2,4 +2,4 @@
// WITH_RUNTIME
class Test {
var x: Int<caret>
}
}
+1 -1
View File
@@ -6,4 +6,4 @@ class Test {
<caret>TODO()
}
set(value) {}
}
}
@@ -4,4 +4,5 @@ class Test {
get() {
return 1
}
}
}
/* IGNORE_FIR */
@@ -5,4 +5,5 @@ class Test {
return 1
}
set(value) {<caret>}
}
}
/* IGNORE_FIR */
@@ -3,4 +3,5 @@
class Test {
var x: Int<caret>
set(value) {}
}
}
/* IGNORE_FIR */
@@ -6,4 +6,5 @@ class Test {
<caret>TODO()
}
set(value) {}
}
}
/* IGNORE_FIR */