diff --git a/idea/resources/intentionDescriptions/ConvertLateinitPropertyToNullableIntention/after.kt.template b/idea/resources/intentionDescriptions/ConvertLateinitPropertyToNullableIntention/after.kt.template new file mode 100644 index 00000000000..ddc0bbec761 --- /dev/null +++ b/idea/resources/intentionDescriptions/ConvertLateinitPropertyToNullableIntention/after.kt.template @@ -0,0 +1,3 @@ +class C { + var foo: String? = null +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/ConvertLateinitPropertyToNullableIntention/before.kt.template b/idea/resources/intentionDescriptions/ConvertLateinitPropertyToNullableIntention/before.kt.template new file mode 100644 index 00000000000..8101cb1e5fd --- /dev/null +++ b/idea/resources/intentionDescriptions/ConvertLateinitPropertyToNullableIntention/before.kt.template @@ -0,0 +1,3 @@ +class C { + lateinit var foo: String +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/ConvertLateinitPropertyToNullableIntention/description.html b/idea/resources/intentionDescriptions/ConvertLateinitPropertyToNullableIntention/description.html new file mode 100644 index 00000000000..fc5cf3875f0 --- /dev/null +++ b/idea/resources/intentionDescriptions/ConvertLateinitPropertyToNullableIntention/description.html @@ -0,0 +1,5 @@ + + +This intention converts a lateinit var property / variable to a nullable var property / variable. + + \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/ConvertNullablePropertyToLateinitIntention/after.kt.template b/idea/resources/intentionDescriptions/ConvertNullablePropertyToLateinitIntention/after.kt.template new file mode 100644 index 00000000000..8101cb1e5fd --- /dev/null +++ b/idea/resources/intentionDescriptions/ConvertNullablePropertyToLateinitIntention/after.kt.template @@ -0,0 +1,3 @@ +class C { + lateinit var foo: String +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/ConvertNullablePropertyToLateinitIntention/before.kt.template b/idea/resources/intentionDescriptions/ConvertNullablePropertyToLateinitIntention/before.kt.template new file mode 100644 index 00000000000..ddc0bbec761 --- /dev/null +++ b/idea/resources/intentionDescriptions/ConvertNullablePropertyToLateinitIntention/before.kt.template @@ -0,0 +1,3 @@ +class C { + var foo: String? = null +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/ConvertNullablePropertyToLateinitIntention/description.html b/idea/resources/intentionDescriptions/ConvertNullablePropertyToLateinitIntention/description.html new file mode 100644 index 00000000000..96b20fca46e --- /dev/null +++ b/idea/resources/intentionDescriptions/ConvertNullablePropertyToLateinitIntention/description.html @@ -0,0 +1,5 @@ + + +This intention converts a nullable var property / variable to a lateinit var property / variable. + + \ No newline at end of file diff --git a/idea/src/META-INF/plugin-common.xml b/idea/src/META-INF/plugin-common.xml index 62dcd0637f2..6873e50d897 100644 --- a/idea/src/META-INF/plugin-common.xml +++ b/idea/src/META-INF/plugin-common.xml @@ -1670,6 +1670,16 @@ Kotlin + + org.jetbrains.kotlin.idea.intentions.ConvertNullablePropertyToLateinitIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ConvertLateinitPropertyToNullableIntention + Kotlin + + ( + KtProperty::class.java, "Convert to nullable var" +) { + override fun isApplicableTo(element: KtProperty, caretOffset: Int): Boolean { + return element.hasModifier(KtTokens.LATEINIT_KEYWORD) + && element.isVar + && element.typeReference?.typeElement !is KtNullableType + && element.initializer == null + } + + override fun applyTo(element: KtProperty, editor: Editor?) { + val typeReference: KtTypeReference = element.typeReference ?: return + val nullableType = element.analyze(BodyResolveMode.PARTIAL)[BindingContext.TYPE, typeReference]?.makeNullable() ?: return + element.removeModifier(KtTokens.LATEINIT_KEYWORD) + element.setType(nullableType) + element.initializer = KtPsiFactory(element).createExpression(KtTokens.NULL_KEYWORD.value) + } +} diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertNullablePropertyToLateinitIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertNullablePropertyToLateinitIntention.kt new file mode 100644 index 00000000000..657ccf79fcf --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertNullablePropertyToLateinitIntention.kt @@ -0,0 +1,54 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. 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 + +import com.intellij.openapi.editor.Editor +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.core.setType +import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isNullExpression +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.KtNullableType +import org.jetbrains.kotlin.psi.KtProperty +import org.jetbrains.kotlin.psi.KtTypeReference +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.isInlineClassType +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode +import org.jetbrains.kotlin.types.TypeUtils +import org.jetbrains.kotlin.types.typeUtil.makeNotNullable + +class ConvertNullablePropertyToLateinitIntention : SelfTargetingIntention( + KtProperty::class.java, "Convert to lateinit var" +) { + override fun isApplicableTo(element: KtProperty, caretOffset: Int): Boolean { + if (element.hasModifier(KtTokens.LATEINIT_KEYWORD) || element.hasModifier(KtTokens.ABSTRACT_KEYWORD)) return false + if (!element.isVar) return false + if (element.isLocal || element.isTopLevel) return false + if (element.getter?.hasBody() != null || element.setter?.hasBody() != null) return false + if (!element.initializer.isNullExpression()) return false + + val typeReference = element.typeReference + if (typeReference?.typeElement !is KtNullableType) return false + val context = element.analyze(BodyResolveMode.PARTIAL) + val type = context[BindingContext.TYPE, typeReference]?.makeNotNullable() ?: return false + if (KotlinBuiltIns.isPrimitiveType(type) || type.isInlineClassType() || TypeUtils.isNullableType(type)) return false + + val descriptor = context[BindingContext.DECLARATION_TO_DESCRIPTOR, element] as? PropertyDescriptor ?: return false + if (context[BindingContext.BACKING_FIELD_REQUIRED, descriptor] == false) return false + if (descriptor.extensionReceiverParameter != null) return false + + return true + } + + override fun applyTo(element: KtProperty, editor: Editor?) { + val typeReference: KtTypeReference = element.typeReference ?: return + val notNullableType = element.analyze(BodyResolveMode.PARTIAL)[BindingContext.TYPE, typeReference]?.makeNotNullable() ?: return + element.addModifier(KtTokens.LATEINIT_KEYWORD) + element.setType(notNullableType) + element.initializer = null + } +} diff --git a/idea/testData/intentions/convertLateinitPropertyToNullable/.intention b/idea/testData/intentions/convertLateinitPropertyToNullable/.intention new file mode 100644 index 00000000000..53a51762f47 --- /dev/null +++ b/idea/testData/intentions/convertLateinitPropertyToNullable/.intention @@ -0,0 +1 @@ +org.jetbrains.kotlin.idea.intentions.ConvertLateinitPropertyToNullableIntention diff --git a/idea/testData/intentions/convertLateinitPropertyToNullable/initializer.kt b/idea/testData/intentions/convertLateinitPropertyToNullable/initializer.kt new file mode 100644 index 00000000000..a7bf10a3041 --- /dev/null +++ b/idea/testData/intentions/convertLateinitPropertyToNullable/initializer.kt @@ -0,0 +1,5 @@ +// IS_APPLICABLE: false +// DISABLE-ERRORS +class C { + private lateinit var bar: String = "" +} \ No newline at end of file diff --git a/idea/testData/intentions/convertLateinitPropertyToNullable/nullable.kt b/idea/testData/intentions/convertLateinitPropertyToNullable/nullable.kt new file mode 100644 index 00000000000..70118185e28 --- /dev/null +++ b/idea/testData/intentions/convertLateinitPropertyToNullable/nullable.kt @@ -0,0 +1,5 @@ +// IS_APPLICABLE: false +// DISABLE-ERRORS +class C { + private lateinit var bar: String? +} \ No newline at end of file diff --git a/idea/testData/intentions/convertLateinitPropertyToNullable/simple.kt b/idea/testData/intentions/convertLateinitPropertyToNullable/simple.kt new file mode 100644 index 00000000000..0dd6a09f3a4 --- /dev/null +++ b/idea/testData/intentions/convertLateinitPropertyToNullable/simple.kt @@ -0,0 +1,3 @@ +class C { + private lateinit var bar: String +} \ No newline at end of file diff --git a/idea/testData/intentions/convertLateinitPropertyToNullable/simple.kt.after b/idea/testData/intentions/convertLateinitPropertyToNullable/simple.kt.after new file mode 100644 index 00000000000..2e6cdfecdd8 --- /dev/null +++ b/idea/testData/intentions/convertLateinitPropertyToNullable/simple.kt.after @@ -0,0 +1,3 @@ +class C { + private var bar: String? = null +} \ No newline at end of file diff --git a/idea/testData/intentions/convertLateinitPropertyToNullable/val.kt b/idea/testData/intentions/convertLateinitPropertyToNullable/val.kt new file mode 100644 index 00000000000..5d02d0da244 --- /dev/null +++ b/idea/testData/intentions/convertLateinitPropertyToNullable/val.kt @@ -0,0 +1,5 @@ +// IS_APPLICABLE: false +// DISABLE-ERRORS +class C { + private lateinit val bar: String +} \ No newline at end of file diff --git a/idea/testData/intentions/convertNullablePropertyToLateinit/.intention b/idea/testData/intentions/convertNullablePropertyToLateinit/.intention new file mode 100644 index 00000000000..1c84b0dc707 --- /dev/null +++ b/idea/testData/intentions/convertNullablePropertyToLateinit/.intention @@ -0,0 +1 @@ +org.jetbrains.kotlin.idea.intentions.ConvertNullablePropertyToLateinitIntention diff --git a/idea/testData/intentions/convertNullablePropertyToLateinit/abstract.kt b/idea/testData/intentions/convertNullablePropertyToLateinit/abstract.kt new file mode 100644 index 00000000000..11a44a7e5cd --- /dev/null +++ b/idea/testData/intentions/convertNullablePropertyToLateinit/abstract.kt @@ -0,0 +1,5 @@ +// IS_APPLICABLE: false +// DISABLE-ERRORS +abstract class C { + abstract var foo: String? = null +} \ No newline at end of file diff --git a/idea/testData/intentions/convertNullablePropertyToLateinit/delegate.kt b/idea/testData/intentions/convertNullablePropertyToLateinit/delegate.kt new file mode 100644 index 00000000000..5a724199b4d --- /dev/null +++ b/idea/testData/intentions/convertNullablePropertyToLateinit/delegate.kt @@ -0,0 +1,11 @@ +// IS_APPLICABLE: false +import kotlin.reflect.KProperty + +class C { + var foo by Delegate +} + +object Delegate { + operator fun getValue(instance: Any?, property: KProperty<*>): String = "" + operator fun setValue(instance: Any?, property: KProperty<*>, value: String) {} +} \ No newline at end of file diff --git a/idea/testData/intentions/convertNullablePropertyToLateinit/extension.kt b/idea/testData/intentions/convertNullablePropertyToLateinit/extension.kt new file mode 100644 index 00000000000..fd427f229bb --- /dev/null +++ b/idea/testData/intentions/convertNullablePropertyToLateinit/extension.kt @@ -0,0 +1,7 @@ +// IS_APPLICABLE: false +// DISABLE-ERRORS +class C { + var Foo.foo: String? = null +} + +class Foo \ No newline at end of file diff --git a/idea/testData/intentions/convertNullablePropertyToLateinit/getter.kt b/idea/testData/intentions/convertNullablePropertyToLateinit/getter.kt new file mode 100644 index 00000000000..85fc84fc9a6 --- /dev/null +++ b/idea/testData/intentions/convertNullablePropertyToLateinit/getter.kt @@ -0,0 +1,5 @@ +// IS_APPLICABLE: false +class C { + var foo: String? = null + get() = "" +} diff --git a/idea/testData/intentions/convertNullablePropertyToLateinit/inlineClass.kt b/idea/testData/intentions/convertNullablePropertyToLateinit/inlineClass.kt new file mode 100644 index 00000000000..d604fa681b2 --- /dev/null +++ b/idea/testData/intentions/convertNullablePropertyToLateinit/inlineClass.kt @@ -0,0 +1,6 @@ +// IS_APPLICABLE: false +class C { + var foo: Foo? = null +} + +inline class Foo(val value: String) \ No newline at end of file diff --git a/idea/testData/intentions/convertNullablePropertyToLateinit/int.kt b/idea/testData/intentions/convertNullablePropertyToLateinit/int.kt new file mode 100644 index 00000000000..246864b8850 --- /dev/null +++ b/idea/testData/intentions/convertNullablePropertyToLateinit/int.kt @@ -0,0 +1,4 @@ +// IS_APPLICABLE: false +class C { + var foo: Int? = null +} \ No newline at end of file diff --git a/idea/testData/intentions/convertNullablePropertyToLateinit/local.kt b/idea/testData/intentions/convertNullablePropertyToLateinit/local.kt new file mode 100644 index 00000000000..2c7dcc1044a --- /dev/null +++ b/idea/testData/intentions/convertNullablePropertyToLateinit/local.kt @@ -0,0 +1,4 @@ +// IS_APPLICABLE: false +fun test() { + var foo: String? = null +} \ No newline at end of file diff --git a/idea/testData/intentions/convertNullablePropertyToLateinit/nonNullInitializer.kt b/idea/testData/intentions/convertNullablePropertyToLateinit/nonNullInitializer.kt new file mode 100644 index 00000000000..1cc9474480a --- /dev/null +++ b/idea/testData/intentions/convertNullablePropertyToLateinit/nonNullInitializer.kt @@ -0,0 +1,4 @@ +// IS_APPLICABLE: false +class C { + private var foo: String? = "" +} \ No newline at end of file diff --git a/idea/testData/intentions/convertNullablePropertyToLateinit/nonNullable.kt b/idea/testData/intentions/convertNullablePropertyToLateinit/nonNullable.kt new file mode 100644 index 00000000000..6a80875b84c --- /dev/null +++ b/idea/testData/intentions/convertNullablePropertyToLateinit/nonNullable.kt @@ -0,0 +1,4 @@ +// IS_APPLICABLE: false +class C { + private var foo: String = "" +} \ No newline at end of file diff --git a/idea/testData/intentions/convertNullablePropertyToLateinit/nullableUpperBound.kt b/idea/testData/intentions/convertNullablePropertyToLateinit/nullableUpperBound.kt new file mode 100644 index 00000000000..ba22f1fa883 --- /dev/null +++ b/idea/testData/intentions/convertNullablePropertyToLateinit/nullableUpperBound.kt @@ -0,0 +1,4 @@ +// IS_APPLICABLE: false +class C { + var foo: V? = null +} \ No newline at end of file diff --git a/idea/testData/intentions/convertNullablePropertyToLateinit/setter.kt b/idea/testData/intentions/convertNullablePropertyToLateinit/setter.kt new file mode 100644 index 00000000000..653026f2f9e --- /dev/null +++ b/idea/testData/intentions/convertNullablePropertyToLateinit/setter.kt @@ -0,0 +1,5 @@ +// IS_APPLICABLE: false +class C { + var foo: String? = null + private set +} \ No newline at end of file diff --git a/idea/testData/intentions/convertNullablePropertyToLateinit/simple.kt b/idea/testData/intentions/convertNullablePropertyToLateinit/simple.kt new file mode 100644 index 00000000000..781326bde52 --- /dev/null +++ b/idea/testData/intentions/convertNullablePropertyToLateinit/simple.kt @@ -0,0 +1,3 @@ +class C { + private var foo: String? = null +} \ No newline at end of file diff --git a/idea/testData/intentions/convertNullablePropertyToLateinit/simple.kt.after b/idea/testData/intentions/convertNullablePropertyToLateinit/simple.kt.after new file mode 100644 index 00000000000..8944523e1be --- /dev/null +++ b/idea/testData/intentions/convertNullablePropertyToLateinit/simple.kt.after @@ -0,0 +1,3 @@ +class C { + private lateinit var foo: String +} \ No newline at end of file diff --git a/idea/testData/intentions/convertNullablePropertyToLateinit/topLevel.kt b/idea/testData/intentions/convertNullablePropertyToLateinit/topLevel.kt new file mode 100644 index 00000000000..eb0d9a45001 --- /dev/null +++ b/idea/testData/intentions/convertNullablePropertyToLateinit/topLevel.kt @@ -0,0 +1,2 @@ +// IS_APPLICABLE: false +var foo: String? = null \ No newline at end of file diff --git a/idea/testData/intentions/convertNullablePropertyToLateinit/unsignedInt.kt b/idea/testData/intentions/convertNullablePropertyToLateinit/unsignedInt.kt new file mode 100644 index 00000000000..6d1f1566aeb --- /dev/null +++ b/idea/testData/intentions/convertNullablePropertyToLateinit/unsignedInt.kt @@ -0,0 +1,5 @@ +// WITH_RUNTIME +// IS_APPLICABLE: false +class C { + var foo: UInt? = null +} \ No newline at end of file diff --git a/idea/testData/intentions/convertNullablePropertyToLateinit/val.kt b/idea/testData/intentions/convertNullablePropertyToLateinit/val.kt new file mode 100644 index 00000000000..343a5c29fdd --- /dev/null +++ b/idea/testData/intentions/convertNullablePropertyToLateinit/val.kt @@ -0,0 +1,4 @@ +// IS_APPLICABLE: false +class C { + private val foo: String? = null +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java index d408cfc8a85..d9e10a40bab 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -5714,6 +5714,39 @@ public class IntentionTestGenerated extends AbstractIntentionTest { } } + @TestMetadata("idea/testData/intentions/convertLateinitPropertyToNullable") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ConvertLateinitPropertyToNullable extends AbstractIntentionTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath); + } + + public void testAllFilesPresentInConvertLateinitPropertyToNullable() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/convertLateinitPropertyToNullable"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true); + } + + @TestMetadata("initializer.kt") + public void testInitializer() throws Exception { + runTest("idea/testData/intentions/convertLateinitPropertyToNullable/initializer.kt"); + } + + @TestMetadata("nullable.kt") + public void testNullable() throws Exception { + runTest("idea/testData/intentions/convertLateinitPropertyToNullable/nullable.kt"); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + runTest("idea/testData/intentions/convertLateinitPropertyToNullable/simple.kt"); + } + + @TestMetadata("val.kt") + public void testVal() throws Exception { + runTest("idea/testData/intentions/convertLateinitPropertyToNullable/val.kt"); + } + } + @TestMetadata("idea/testData/intentions/convertLineCommentToBlockComment") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) @@ -5830,6 +5863,94 @@ public class IntentionTestGenerated extends AbstractIntentionTest { } } + @TestMetadata("idea/testData/intentions/convertNullablePropertyToLateinit") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ConvertNullablePropertyToLateinit extends AbstractIntentionTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath); + } + + @TestMetadata("abstract.kt") + public void testAbstract() throws Exception { + runTest("idea/testData/intentions/convertNullablePropertyToLateinit/abstract.kt"); + } + + public void testAllFilesPresentInConvertNullablePropertyToLateinit() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/convertNullablePropertyToLateinit"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true); + } + + @TestMetadata("delegate.kt") + public void testDelegate() throws Exception { + runTest("idea/testData/intentions/convertNullablePropertyToLateinit/delegate.kt"); + } + + @TestMetadata("extension.kt") + public void testExtension() throws Exception { + runTest("idea/testData/intentions/convertNullablePropertyToLateinit/extension.kt"); + } + + @TestMetadata("getter.kt") + public void testGetter() throws Exception { + runTest("idea/testData/intentions/convertNullablePropertyToLateinit/getter.kt"); + } + + @TestMetadata("inlineClass.kt") + public void testInlineClass() throws Exception { + runTest("idea/testData/intentions/convertNullablePropertyToLateinit/inlineClass.kt"); + } + + @TestMetadata("int.kt") + public void testInt() throws Exception { + runTest("idea/testData/intentions/convertNullablePropertyToLateinit/int.kt"); + } + + @TestMetadata("local.kt") + public void testLocal() throws Exception { + runTest("idea/testData/intentions/convertNullablePropertyToLateinit/local.kt"); + } + + @TestMetadata("nonNullInitializer.kt") + public void testNonNullInitializer() throws Exception { + runTest("idea/testData/intentions/convertNullablePropertyToLateinit/nonNullInitializer.kt"); + } + + @TestMetadata("nonNullable.kt") + public void testNonNullable() throws Exception { + runTest("idea/testData/intentions/convertNullablePropertyToLateinit/nonNullable.kt"); + } + + @TestMetadata("nullableUpperBound.kt") + public void testNullableUpperBound() throws Exception { + runTest("idea/testData/intentions/convertNullablePropertyToLateinit/nullableUpperBound.kt"); + } + + @TestMetadata("setter.kt") + public void testSetter() throws Exception { + runTest("idea/testData/intentions/convertNullablePropertyToLateinit/setter.kt"); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + runTest("idea/testData/intentions/convertNullablePropertyToLateinit/simple.kt"); + } + + @TestMetadata("topLevel.kt") + public void testTopLevel() throws Exception { + runTest("idea/testData/intentions/convertNullablePropertyToLateinit/topLevel.kt"); + } + + @TestMetadata("unsignedInt.kt") + public void testUnsignedInt() throws Exception { + runTest("idea/testData/intentions/convertNullablePropertyToLateinit/unsignedInt.kt"); + } + + @TestMetadata("val.kt") + public void testVal() throws Exception { + runTest("idea/testData/intentions/convertNullablePropertyToLateinit/val.kt"); + } + } + @TestMetadata("idea/testData/intentions/convertObjectLiteralToClass") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)