Add "Add getter/setter" quick fix for uninitialized property
#KT-30078 Fixed
This commit is contained in:
committed by
Dmitry Gridin
parent
efcc6f0967
commit
f861b10798
@@ -255,7 +255,10 @@ class KtPsiFactory @JvmOverloads constructor(private val project: Project, val m
|
||||
}
|
||||
|
||||
fun createPropertyGetter(expression: KtExpression): KtPropertyAccessor {
|
||||
val property = createProperty("val x get() = 1")
|
||||
val property = if (expression is KtBlockExpression)
|
||||
createProperty("val x get() {\nreturn 1\n}")
|
||||
else
|
||||
createProperty("val x get() = 1")
|
||||
val getter = property.getter!!
|
||||
val bodyExpression = getter.bodyExpression!!
|
||||
|
||||
|
||||
@@ -16,18 +16,21 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.intentions
|
||||
|
||||
import com.intellij.codeInsight.intention.IntentionAction
|
||||
import com.intellij.codeInsight.intention.LowPriorityAction
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
|
||||
import org.jetbrains.kotlin.idea.quickfix.KotlinSingleIntentionActionFactory
|
||||
import org.jetbrains.kotlin.idea.refactoring.isAbstract
|
||||
import org.jetbrains.kotlin.idea.util.findAnnotation
|
||||
import org.jetbrains.kotlin.idea.util.hasJvmFieldAnnotation
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.KtProperty
|
||||
import org.jetbrains.kotlin.psi.KtPropertyAccessor
|
||||
import org.jetbrains.kotlin.psi.KtPsiFactory
|
||||
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||
|
||||
abstract class AbstractAddAccessorsIntention(
|
||||
private val addGetter: Boolean,
|
||||
@@ -45,25 +48,56 @@ abstract class AbstractAddAccessorsIntention(
|
||||
val descriptor = element.resolveToDescriptorIfAny() as? CallableMemberDescriptor ?: return null
|
||||
if (descriptor.isExpect) return null
|
||||
|
||||
val hasInitializer = element.hasInitializer()
|
||||
if (element.typeReference == null && !hasInitializer) return null
|
||||
if (addSetter && (!element.isVar || element.setter != null)) return null
|
||||
if (addGetter && ((element.typeReference == null && element.initializer == null) || element.getter != null)) return null
|
||||
return element.nameIdentifier?.textRange
|
||||
if (addGetter && element.getter != null) return null
|
||||
return if (hasInitializer) element.nameIdentifier?.textRange else element.textRange
|
||||
}
|
||||
|
||||
override fun applyTo(element: KtProperty, editor: Editor?) {
|
||||
val hasInitializer = element.hasInitializer()
|
||||
val psiFactory = KtPsiFactory(element)
|
||||
if (addGetter) {
|
||||
val expression = psiFactory.createExpression("field")
|
||||
val expression = if (hasInitializer) psiFactory.createExpression("field") else psiFactory.createBlock("TODO()")
|
||||
val getter = psiFactory.createPropertyGetter(expression)
|
||||
if (element.setter != null)
|
||||
val added = if (element.setter != null) {
|
||||
element.addBefore(getter, element.setter)
|
||||
else
|
||||
} else {
|
||||
element.add(getter)
|
||||
}
|
||||
if (!hasInitializer) {
|
||||
(added as? KtPropertyAccessor)?.bodyBlockExpression?.statements?.firstOrNull()?.let {
|
||||
editor?.caretModel?.moveToOffset(it.startOffset)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (addSetter) {
|
||||
val expression = psiFactory.createBlock("field = value")
|
||||
val expression = if (hasInitializer) psiFactory.createBlock("field = value") else psiFactory.createEmptyBody()
|
||||
val setter = psiFactory.createPropertySetter(expression)
|
||||
element.add(setter)
|
||||
val added = element.add(setter)
|
||||
if (!hasInitializer && !addGetter) {
|
||||
(added as? KtPropertyAccessor)?.bodyBlockExpression?.lBrace?.let {
|
||||
editor?.caretModel?.moveToOffset(it.startOffset + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object : KotlinSingleIntentionActionFactory() {
|
||||
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
|
||||
val property = diagnostic.psiElement as? KtProperty ?: return null
|
||||
return if (property.isVar) {
|
||||
val getter = property.getter
|
||||
val setter = property.setter
|
||||
when {
|
||||
getter == null && setter == null -> AddPropertyAccessorsIntention()
|
||||
getter == null -> AddPropertyGetterIntention()
|
||||
else -> AddPropertySetterIntention()
|
||||
}
|
||||
} else {
|
||||
AddPropertyGetterIntention()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.diagnostics.Errors.*
|
||||
import org.jetbrains.kotlin.idea.core.overrideImplement.ImplementAsConstructorParameter
|
||||
import org.jetbrains.kotlin.idea.core.overrideImplement.ImplementMembersHandler
|
||||
import org.jetbrains.kotlin.idea.inspections.*
|
||||
import org.jetbrains.kotlin.idea.intentions.AbstractAddAccessorsIntention
|
||||
import org.jetbrains.kotlin.idea.intentions.AddValVarToConstructorParameterAction
|
||||
import org.jetbrains.kotlin.idea.intentions.ConvertPropertyInitializerToGetterIntention
|
||||
import org.jetbrains.kotlin.idea.intentions.MoveMemberToCompanionObjectIntention
|
||||
@@ -603,5 +604,8 @@ class QuickFixRegistrar : QuickFixContributor {
|
||||
CONSTANT_EXPECTED_TYPE_MISMATCH.registerFactory(SurroundWithLambdaFix)
|
||||
|
||||
NO_SET_METHOD.registerFactory(ChangeToMutableCollectionFix)
|
||||
|
||||
MUST_BE_INITIALIZED_OR_BE_ABSTRACT.registerFactory(AbstractAddAccessorsIntention)
|
||||
MUST_BE_INITIALIZED.registerFactory(AbstractAddAccessorsIntention)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// IS_APPLICABLE: false
|
||||
// SKIP_ERRORS_BEFORE
|
||||
// SKIP_ERRORS_AFTER
|
||||
var x<caret>
|
||||
@@ -1,6 +0,0 @@
|
||||
// SKIP_ERRORS_BEFORE
|
||||
// SKIP_ERRORS_AFTER
|
||||
var x
|
||||
set(value) {
|
||||
field = value
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
// "Add getter" "true"
|
||||
// WITH_RUNTIME
|
||||
class Test {
|
||||
val x: Int<caret>
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Add getter" "true"
|
||||
// WITH_RUNTIME
|
||||
class Test {
|
||||
val x: Int
|
||||
get() {
|
||||
<caret>TODO()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
// "Add getter and setter" "true"
|
||||
// WITH_RUNTIME
|
||||
class Test {
|
||||
var x: Int<caret>
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// "Add getter and setter" "true"
|
||||
// WITH_RUNTIME
|
||||
class Test {
|
||||
var x: Int
|
||||
get() {
|
||||
<caret>TODO()
|
||||
}
|
||||
set(value) {}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// "Add setter" "true"
|
||||
class Test {
|
||||
var x: Int<caret>
|
||||
get() {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Add setter" "true"
|
||||
class Test {
|
||||
var x: Int
|
||||
get() {
|
||||
return 1
|
||||
}
|
||||
set(value) {<caret>}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// "Add getter" "true"
|
||||
// WITH_RUNTIME
|
||||
class Test {
|
||||
var x: Int<caret>
|
||||
set(value) {}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// "Add getter" "true"
|
||||
// WITH_RUNTIME
|
||||
class Test {
|
||||
var x: Int
|
||||
get() {
|
||||
<caret>TODO()
|
||||
}
|
||||
set(value) {}
|
||||
}
|
||||
@@ -3,6 +3,9 @@
|
||||
// ACTION: Make 'a' abstract
|
||||
// ACTION: Move to constructor parameters
|
||||
// ACTION: Move to constructor
|
||||
// ACTION: Add getter
|
||||
// ACTION: Add getter and setter
|
||||
// ACTION: Add setter
|
||||
// ERROR: Property must be initialized or be abstract
|
||||
|
||||
class A {
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
// ACTION: Make 'a' abstract
|
||||
// ACTION: Move to constructor parameters
|
||||
// ACTION: Move to constructor
|
||||
// ACTION: Add getter
|
||||
// ACTION: Add getter and setter
|
||||
// ACTION: Add setter
|
||||
// ERROR: Property must be initialized or be abstract
|
||||
|
||||
class A {
|
||||
|
||||
+13
@@ -221,6 +221,19 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/quickfix/addPropertyAccessors")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class AddPropertyAccessors extends AbstractQuickFixMultiFileTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTestWithExtraFile, TargetBackend.ANY, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInAddPropertyAccessors() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/addPropertyAccessors"), Pattern.compile("^(\\w+)\\.((before\\.Main\\.\\w+)|(test))$"), TargetBackend.ANY, true);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/quickfix/addPropertyToSupertype")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
@@ -846,6 +846,39 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/quickfix/addPropertyAccessors")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class AddPropertyAccessors extends AbstractQuickFixTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInAddPropertyAccessors() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/addPropertyAccessors"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, 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");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/quickfix/addPropertyToSupertype")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Reference in New Issue
Block a user