diff --git a/idea/resources/intentionDescriptions/ConvertPropertyGetterToInitializerIntention/after.kt.template b/idea/resources/intentionDescriptions/ConvertPropertyGetterToInitializerIntention/after.kt.template new file mode 100644 index 00000000000..45a9b7d4aa5 --- /dev/null +++ b/idea/resources/intentionDescriptions/ConvertPropertyGetterToInitializerIntention/after.kt.template @@ -0,0 +1,3 @@ +class A { + val foo: Int = 1 +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/ConvertPropertyGetterToInitializerIntention/before.kt.template b/idea/resources/intentionDescriptions/ConvertPropertyGetterToInitializerIntention/before.kt.template new file mode 100644 index 00000000000..57717f73075 --- /dev/null +++ b/idea/resources/intentionDescriptions/ConvertPropertyGetterToInitializerIntention/before.kt.template @@ -0,0 +1,4 @@ +class A { + val foo: Int + get() = 1 +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/ConvertPropertyGetterToInitializerIntention/description.html b/idea/resources/intentionDescriptions/ConvertPropertyGetterToInitializerIntention/description.html new file mode 100644 index 00000000000..bb3401bdc9c --- /dev/null +++ b/idea/resources/intentionDescriptions/ConvertPropertyGetterToInitializerIntention/description.html @@ -0,0 +1,5 @@ + + +This intention converts a property getter to to a initializer. + + \ No newline at end of file diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 94a9dab6b3b..4708368f36f 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -1676,6 +1676,11 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio. Kotlin + + org.jetbrains.kotlin.idea.intentions.ConvertPropertyGetterToInitializerIntention + Kotlin + + Kotlin + + org.jetbrains.kotlin.idea.intentions.ConvertPropertyGetterToInitializerIntention + Kotlin + + Kotlin + + org.jetbrains.kotlin.idea.intentions.ConvertPropertyGetterToInitializerIntention + Kotlin + + Kotlin + + org.jetbrains.kotlin.idea.intentions.ConvertPropertyGetterToInitializerIntention + Kotlin + + Kotlin + + org.jetbrains.kotlin.idea.intentions.ConvertPropertyGetterToInitializerIntention + Kotlin + + Kotlin + + org.jetbrains.kotlin.idea.intentions.ConvertPropertyGetterToInitializerIntention + Kotlin + + Kotlin + + org.jetbrains.kotlin.idea.intentions.ConvertPropertyGetterToInitializerIntention + Kotlin + + ( + KtPropertyAccessor::class.java, "Convert property getter to initializer" +) { + + override fun isApplicableTo(element: KtPropertyAccessor, caretOffset: Int): Boolean { + if (!element.isGetter || element.singleExpression() == null) return false + + val property = element.parent as? KtProperty ?: return false + if (property.hasInitializer() + || property.receiverTypeReference != null + || property.containingClass()?.isInterface() == true + || (property.descriptor as? PropertyDescriptor)?.isExpect == true + ) return false + + return true + } + + override fun applyTo(element: KtPropertyAccessor, editor: Editor?) { + val property = element.parent as? KtProperty ?: return + val commentSaver = CommentSaver(property) + property.initializer = element.singleExpression() + property.deleteChildRange(property.initializer?.nextSibling, element) + editor?.caretModel?.moveToOffset(property.endOffset) + commentSaver.restore(property) + } +} + +private fun KtPropertyAccessor.singleExpression(): KtExpression? { + val bodyExpression = this.bodyExpression + return if (bodyExpression is KtBlockExpression) + (bodyExpression.statements.singleOrNull() as? KtReturnExpression)?.returnedExpression + else + bodyExpression +} diff --git a/idea/testData/intentions/convertPropertyGetterToInitializer/.intention b/idea/testData/intentions/convertPropertyGetterToInitializer/.intention new file mode 100644 index 00000000000..514a3e8b68a --- /dev/null +++ b/idea/testData/intentions/convertPropertyGetterToInitializer/.intention @@ -0,0 +1 @@ +org.jetbrains.kotlin.idea.intentions.ConvertPropertyGetterToInitializerIntention \ No newline at end of file diff --git a/idea/testData/intentions/convertPropertyGetterToInitializer/block.kt b/idea/testData/intentions/convertPropertyGetterToInitializer/block.kt new file mode 100644 index 00000000000..9fef4a2a399 --- /dev/null +++ b/idea/testData/intentions/convertPropertyGetterToInitializer/block.kt @@ -0,0 +1,4 @@ +val p: Int + get() { + return 1 + } \ No newline at end of file diff --git a/idea/testData/intentions/convertPropertyGetterToInitializer/block.kt.after b/idea/testData/intentions/convertPropertyGetterToInitializer/block.kt.after new file mode 100644 index 00000000000..fdb21f903ac --- /dev/null +++ b/idea/testData/intentions/convertPropertyGetterToInitializer/block.kt.after @@ -0,0 +1 @@ +val p: Int = 1 \ No newline at end of file diff --git a/idea/testData/intentions/convertPropertyGetterToInitializer/extentionProperty.kt b/idea/testData/intentions/convertPropertyGetterToInitializer/extentionProperty.kt new file mode 100644 index 00000000000..364c4bdfff5 --- /dev/null +++ b/idea/testData/intentions/convertPropertyGetterToInitializer/extentionProperty.kt @@ -0,0 +1,5 @@ +// IS_APPLICABLE: false +class C + +val C.p: Int + get() = 1 \ No newline at end of file diff --git a/idea/testData/intentions/convertPropertyGetterToInitializer/hasComment.kt b/idea/testData/intentions/convertPropertyGetterToInitializer/hasComment.kt new file mode 100644 index 00000000000..9aa8d6cc95b --- /dev/null +++ b/idea/testData/intentions/convertPropertyGetterToInitializer/hasComment.kt @@ -0,0 +1,2 @@ +val p: Int // comment + get() = 1 \ No newline at end of file diff --git a/idea/testData/intentions/convertPropertyGetterToInitializer/hasComment.kt.after b/idea/testData/intentions/convertPropertyGetterToInitializer/hasComment.kt.after new file mode 100644 index 00000000000..f54ecd58ed1 --- /dev/null +++ b/idea/testData/intentions/convertPropertyGetterToInitializer/hasComment.kt.after @@ -0,0 +1,2 @@ +val p: Int // comment + = 1 \ No newline at end of file diff --git a/idea/testData/intentions/convertPropertyGetterToInitializer/hasComment2.kt b/idea/testData/intentions/convertPropertyGetterToInitializer/hasComment2.kt new file mode 100644 index 00000000000..01b29ea9a81 --- /dev/null +++ b/idea/testData/intentions/convertPropertyGetterToInitializer/hasComment2.kt @@ -0,0 +1,3 @@ +val p2: Int +// comment + get() = 1 \ No newline at end of file diff --git a/idea/testData/intentions/convertPropertyGetterToInitializer/hasComment2.kt.after b/idea/testData/intentions/convertPropertyGetterToInitializer/hasComment2.kt.after new file mode 100644 index 00000000000..cee32b2edcb --- /dev/null +++ b/idea/testData/intentions/convertPropertyGetterToInitializer/hasComment2.kt.after @@ -0,0 +1,2 @@ +val p2: Int// comment + = 1 \ No newline at end of file diff --git a/idea/testData/intentions/convertPropertyGetterToInitializer/hasComment3.kt b/idea/testData/intentions/convertPropertyGetterToInitializer/hasComment3.kt new file mode 100644 index 00000000000..3d9ce5bba3d --- /dev/null +++ b/idea/testData/intentions/convertPropertyGetterToInitializer/hasComment3.kt @@ -0,0 +1,2 @@ +val p3: Int + get() = 1 // comment \ No newline at end of file diff --git a/idea/testData/intentions/convertPropertyGetterToInitializer/hasComment3.kt.after b/idea/testData/intentions/convertPropertyGetterToInitializer/hasComment3.kt.after new file mode 100644 index 00000000000..cc44eb9151d --- /dev/null +++ b/idea/testData/intentions/convertPropertyGetterToInitializer/hasComment3.kt.after @@ -0,0 +1 @@ +val p3: Int = 1 // comment \ No newline at end of file diff --git a/idea/testData/intentions/convertPropertyGetterToInitializer/hasInitializer.kt b/idea/testData/intentions/convertPropertyGetterToInitializer/hasInitializer.kt new file mode 100644 index 00000000000..69709f1fc65 --- /dev/null +++ b/idea/testData/intentions/convertPropertyGetterToInitializer/hasInitializer.kt @@ -0,0 +1,3 @@ +// IS_APPLICABLE: false +var hasInitializer: Int = 0 + get() = 1 \ No newline at end of file diff --git a/idea/testData/intentions/convertPropertyGetterToInitializer/inInterface.kt b/idea/testData/intentions/convertPropertyGetterToInitializer/inInterface.kt new file mode 100644 index 00000000000..03f94f89426 --- /dev/null +++ b/idea/testData/intentions/convertPropertyGetterToInitializer/inInterface.kt @@ -0,0 +1,5 @@ +// IS_APPLICABLE: false +interface I { + val p: Int + get() = 1 +} \ No newline at end of file diff --git a/idea/testData/intentions/convertPropertyGetterToInitializer/multiStatementBlock.kt b/idea/testData/intentions/convertPropertyGetterToInitializer/multiStatementBlock.kt new file mode 100644 index 00000000000..f83127639c6 --- /dev/null +++ b/idea/testData/intentions/convertPropertyGetterToInitializer/multiStatementBlock.kt @@ -0,0 +1,6 @@ +// IS_APPLICABLE: false +val multiStatementBlock: Int + get() { + val a = 1 + return 6 + } \ No newline at end of file diff --git a/idea/testData/intentions/convertPropertyGetterToInitializer/run.kt b/idea/testData/intentions/convertPropertyGetterToInitializer/run.kt new file mode 100644 index 00000000000..90053a9a9eb --- /dev/null +++ b/idea/testData/intentions/convertPropertyGetterToInitializer/run.kt @@ -0,0 +1,3 @@ +// WITH_RUNTIME +val p: Int + get() = run { 1 } \ No newline at end of file diff --git a/idea/testData/intentions/convertPropertyGetterToInitializer/run.kt.after b/idea/testData/intentions/convertPropertyGetterToInitializer/run.kt.after new file mode 100644 index 00000000000..5943b502252 --- /dev/null +++ b/idea/testData/intentions/convertPropertyGetterToInitializer/run.kt.after @@ -0,0 +1,2 @@ +// WITH_RUNTIME +val p: Int = run { 1 } \ No newline at end of file diff --git a/idea/testData/intentions/convertPropertyGetterToInitializer/simple.kt b/idea/testData/intentions/convertPropertyGetterToInitializer/simple.kt new file mode 100644 index 00000000000..7206ca94870 --- /dev/null +++ b/idea/testData/intentions/convertPropertyGetterToInitializer/simple.kt @@ -0,0 +1,2 @@ +val p: Int + get() = 1 \ No newline at end of file diff --git a/idea/testData/intentions/convertPropertyGetterToInitializer/simple.kt.after b/idea/testData/intentions/convertPropertyGetterToInitializer/simple.kt.after new file mode 100644 index 00000000000..75b2b28a0fe --- /dev/null +++ b/idea/testData/intentions/convertPropertyGetterToInitializer/simple.kt.after @@ -0,0 +1 @@ +val p: Int = 1 \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/convertPropertyGetterToInitializer/header/header.kt b/idea/testData/multiModuleQuickFix/convertPropertyGetterToInitializer/header/header.kt new file mode 100644 index 00000000000..89432bb2523 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/convertPropertyGetterToInitializer/header/header.kt @@ -0,0 +1,7 @@ +// "Convert property getter to initializer" "false" +// ERROR: Expected declaration must not have a body +// ACTION: Convert to block body +expect class C { + val p: Int + get() = 1 +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/convertPropertyGetterToInitializer/js/js.kt b/idea/testData/multiModuleQuickFix/convertPropertyGetterToInitializer/js/js.kt new file mode 100644 index 00000000000..181da9fd38f --- /dev/null +++ b/idea/testData/multiModuleQuickFix/convertPropertyGetterToInitializer/js/js.kt @@ -0,0 +1,3 @@ +actual class C { + actual val p: Int = 1 +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/convertPropertyGetterToInitializer/jvm/jvm.kt b/idea/testData/multiModuleQuickFix/convertPropertyGetterToInitializer/jvm/jvm.kt new file mode 100644 index 00000000000..181da9fd38f --- /dev/null +++ b/idea/testData/multiModuleQuickFix/convertPropertyGetterToInitializer/jvm/jvm.kt @@ -0,0 +1,3 @@ +actual class C { + actual val p: Int = 1 +} \ No newline at end of file diff --git a/idea/testData/quickfix/surroundWithNullCheck/unsafeCallInGetter.kt b/idea/testData/quickfix/surroundWithNullCheck/unsafeCallInGetter.kt index ed8e32f3618..f3704618a4d 100644 --- a/idea/testData/quickfix/surroundWithNullCheck/unsafeCallInGetter.kt +++ b/idea/testData/quickfix/surroundWithNullCheck/unsafeCallInGetter.kt @@ -1,5 +1,6 @@ // "Surround with null check" "false" // ACTION: Add non-null asserted (!!) call +// ACTION: Convert property getter to initializer // ACTION: Convert to block body // ACTION: Introduce local variable // ACTION: Replace with safe (?.) call diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java index cd652a7a174..9d5dadfa725 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -5816,6 +5816,69 @@ public class IntentionTestGenerated extends AbstractIntentionTest { } } + @TestMetadata("idea/testData/intentions/convertPropertyGetterToInitializer") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ConvertPropertyGetterToInitializer extends AbstractIntentionTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath); + } + + public void testAllFilesPresentInConvertPropertyGetterToInitializer() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/convertPropertyGetterToInitializer"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true); + } + + @TestMetadata("block.kt") + public void testBlock() throws Exception { + runTest("idea/testData/intentions/convertPropertyGetterToInitializer/block.kt"); + } + + @TestMetadata("extentionProperty.kt") + public void testExtentionProperty() throws Exception { + runTest("idea/testData/intentions/convertPropertyGetterToInitializer/extentionProperty.kt"); + } + + @TestMetadata("hasComment.kt") + public void testHasComment() throws Exception { + runTest("idea/testData/intentions/convertPropertyGetterToInitializer/hasComment.kt"); + } + + @TestMetadata("hasComment2.kt") + public void testHasComment2() throws Exception { + runTest("idea/testData/intentions/convertPropertyGetterToInitializer/hasComment2.kt"); + } + + @TestMetadata("hasComment3.kt") + public void testHasComment3() throws Exception { + runTest("idea/testData/intentions/convertPropertyGetterToInitializer/hasComment3.kt"); + } + + @TestMetadata("hasInitializer.kt") + public void testHasInitializer() throws Exception { + runTest("idea/testData/intentions/convertPropertyGetterToInitializer/hasInitializer.kt"); + } + + @TestMetadata("inInterface.kt") + public void testInInterface() throws Exception { + runTest("idea/testData/intentions/convertPropertyGetterToInitializer/inInterface.kt"); + } + + @TestMetadata("multiStatementBlock.kt") + public void testMultiStatementBlock() throws Exception { + runTest("idea/testData/intentions/convertPropertyGetterToInitializer/multiStatementBlock.kt"); + } + + @TestMetadata("run.kt") + public void testRun() throws Exception { + runTest("idea/testData/intentions/convertPropertyGetterToInitializer/run.kt"); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + runTest("idea/testData/intentions/convertPropertyGetterToInitializer/simple.kt"); + } + } + @TestMetadata("idea/testData/intentions/convertPropertyInitializerToGetter") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiModuleTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiModuleTestGenerated.java index 299508a247d..fa098b7f314 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiModuleTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiModuleTestGenerated.java @@ -144,6 +144,11 @@ public class QuickFixMultiModuleTestGenerated extends AbstractQuickFixMultiModul runTest("idea/testData/multiModuleQuickFix/convertExpectSealedClassToEnum/"); } + @TestMetadata("convertPropertyGetterToInitializer") + public void testConvertPropertyGetterToInitializer() throws Exception { + runTest("idea/testData/multiModuleQuickFix/convertPropertyGetterToInitializer/"); + } + @TestMetadata("createFunInExpectClass") public void testCreateFunInExpectClass() throws Exception { runTest("idea/testData/multiModuleQuickFix/createFunInExpectClass/");