From a4a10c7ba478b0bbe225e7746373249e8279f6ae Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Tue, 17 Apr 2018 15:42:09 +0300 Subject: [PATCH] Create from Usage: Fix generation of extensions with nullable receiver #KT-23796 Fixed --- .../callableBuilder/CallableBuilder.kt | 11 ++++++++--- .../callableBuilder/CallableBuilder.kt.172 | 11 ++++++++--- .../createFunction/call/nullableReceiver.kt | 4 ++++ .../createFunction/call/nullableReceiver.kt.after | 8 ++++++++ .../createVariable/property/nullableReceiver.kt | 6 ++++++ .../property/nullableReceiver.kt.after | 11 +++++++++++ .../kotlin/idea/quickfix/QuickFixTestGenerated.java | 12 ++++++++++++ 7 files changed, 57 insertions(+), 6 deletions(-) create mode 100644 idea/testData/quickfix/createFromUsage/createFunction/call/nullableReceiver.kt create mode 100644 idea/testData/quickfix/createFromUsage/createFunction/call/nullableReceiver.kt.after create mode 100644 idea/testData/quickfix/createFromUsage/createVariable/property/nullableReceiver.kt create mode 100644 idea/testData/quickfix/createFromUsage/createVariable/property/nullableReceiver.kt.after diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableBuilder.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableBuilder.kt index abd8b06f7eb..dea2a3c33cb 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableBuilder.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableBuilder.kt @@ -77,6 +77,7 @@ import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.types.checker.KotlinTypeChecker import org.jetbrains.kotlin.types.typeUtil.isAnyOrNullableAny import org.jetbrains.kotlin.types.typeUtil.isUnit +import org.jetbrains.kotlin.types.typeUtil.makeNullable import java.lang.AssertionError import java.lang.IllegalArgumentException import java.lang.IllegalStateException @@ -243,6 +244,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) { // gather relevant information val placement = placement + var nullableReceiver = false when (placement) { is CallablePlacement.NoReceiver -> { containingElement = placement.containingElement @@ -255,14 +257,17 @@ class CallableBuilder(val config: CallableBuilderConfiguration) { } } is CallablePlacement.WithReceiver -> { - receiverClassDescriptor = - placement.receiverTypeCandidate.theType.constructor.declarationDescriptor + val theType = placement.receiverTypeCandidate.theType + nullableReceiver = theType.isMarkedNullable + receiverClassDescriptor = theType.constructor.declarationDescriptor val classDeclaration = receiverClassDescriptor?.let { DescriptorToSourceUtils.getSourceFromDescriptor(it) } containingElement = if (!config.isExtension && classDeclaration != null) classDeclaration else config.currentFile } else -> throw IllegalArgumentException("Placement wan't initialized") } - val receiverType = receiverClassDescriptor?.defaultType + val receiverType = receiverClassDescriptor?.defaultType?.let { + if (nullableReceiver) it.makeNullable() else it + } val project = config.currentFile.project diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableBuilder.kt.172 b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableBuilder.kt.172 index 2efde613f0e..2598706bb0e 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableBuilder.kt.172 +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableBuilder.kt.172 @@ -77,6 +77,7 @@ import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.types.checker.KotlinTypeChecker import org.jetbrains.kotlin.types.typeUtil.isAnyOrNullableAny import org.jetbrains.kotlin.types.typeUtil.isUnit +import org.jetbrains.kotlin.types.typeUtil.makeNullable import java.lang.AssertionError import java.lang.IllegalArgumentException import java.lang.IllegalStateException @@ -243,6 +244,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) { // gather relevant information val placement = placement + var nullableReceiver = false when (placement) { is CallablePlacement.NoReceiver -> { containingElement = placement.containingElement @@ -255,14 +257,17 @@ class CallableBuilder(val config: CallableBuilderConfiguration) { } } is CallablePlacement.WithReceiver -> { - receiverClassDescriptor = - placement.receiverTypeCandidate.theType.constructor.declarationDescriptor + val theType = placement.receiverTypeCandidate.theType + nullableReceiver = theType.isMarkedNullable + receiverClassDescriptor = theType.constructor.declarationDescriptor val classDeclaration = receiverClassDescriptor?.let { DescriptorToSourceUtils.getSourceFromDescriptor(it) } containingElement = if (!config.isExtension && classDeclaration != null) classDeclaration else config.currentFile } else -> throw IllegalArgumentException("Placement wan't initialized") } - val receiverType = receiverClassDescriptor?.defaultType + val receiverType = receiverClassDescriptor?.defaultType?.let { + if (nullableReceiver) it.makeNullable() else it + } val project = config.currentFile.project diff --git a/idea/testData/quickfix/createFromUsage/createFunction/call/nullableReceiver.kt b/idea/testData/quickfix/createFromUsage/createFunction/call/nullableReceiver.kt new file mode 100644 index 00000000000..5ae872678fd --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/call/nullableReceiver.kt @@ -0,0 +1,4 @@ +// "Create extension function 'String?.notExistingFun'" "true" +fun context(p: String?) { + p.notExistingFun() +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createFunction/call/nullableReceiver.kt.after b/idea/testData/quickfix/createFromUsage/createFunction/call/nullableReceiver.kt.after new file mode 100644 index 00000000000..68581acacd1 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/call/nullableReceiver.kt.after @@ -0,0 +1,8 @@ +// "Create extension function 'String?.notExistingFun'" "true" +fun context(p: String?) { + p.notExistingFun() +} + +private fun String?.notExistingFun() { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. +} diff --git a/idea/testData/quickfix/createFromUsage/createVariable/property/nullableReceiver.kt b/idea/testData/quickfix/createFromUsage/createVariable/property/nullableReceiver.kt new file mode 100644 index 00000000000..34ff5b802ff --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createVariable/property/nullableReceiver.kt @@ -0,0 +1,6 @@ +// "Create extension property 'String?.notExistingVal'" "true" +fun foo(n: Int) {} + +fun context(p: String?) { + foo(p.notExistingVal) +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createVariable/property/nullableReceiver.kt.after b/idea/testData/quickfix/createFromUsage/createVariable/property/nullableReceiver.kt.after new file mode 100644 index 00000000000..9b2e5e79947 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createVariable/property/nullableReceiver.kt.after @@ -0,0 +1,11 @@ +// "Create extension property 'String?.notExistingVal'" "true" +fun foo(n: Int) {} + +fun context(p: String?) { + foo(p.notExistingVal) +} + +private val String?.notExistingVal: Int + get() { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java index 5bb0d6c2af0..25b11dd17d6 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -3084,6 +3084,12 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { runTest("idea/testData/quickfix/createFromUsage/createFunction/call/notExactArgument.kt"); } + @TestMetadata("nullableReceiver.kt") + public void testNullableReceiver() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/nullableReceiver.kt"); + doTest(fileName); + } + @TestMetadata("objectMemberFunNoReceiver.kt") public void testObjectMemberFunNoReceiver() throws Exception { runTest("idea/testData/quickfix/createFromUsage/createFunction/call/objectMemberFunNoReceiver.kt"); @@ -4599,6 +4605,12 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { runTest("idea/testData/quickfix/createFromUsage/createVariable/property/nonAbstractInInterface.kt"); } + @TestMetadata("nullableReceiver.kt") + public void testNullableReceiver() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createVariable/property/nullableReceiver.kt"); + doTest(fileName); + } + @TestMetadata("objectMemberValNoReceiver.kt") public void testObjectMemberValNoReceiver() throws Exception { runTest("idea/testData/quickfix/createFromUsage/createVariable/property/objectMemberValNoReceiver.kt");