From a5a13b74f5ed978e187ad17b317b2fcae64908c1 Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Thu, 10 May 2018 19:16:58 +0300 Subject: [PATCH] Create from Usage: Support smart casts on explicit receivers #KT-24069 Fixed --- .../createFromUsage/callableBuilder/typeUtils.kt | 10 +++++++--- .../CreateCallableFromCallActionFactory.kt | 8 +++++++- .../createFunction/call/receiverWithSmartCast.kt | 10 ++++++++++ .../call/receiverWithSmartCast.kt.after | 14 ++++++++++++++ .../idea/quickfix/QuickFixTestGenerated.java | 5 +++++ 5 files changed, 43 insertions(+), 4 deletions(-) create mode 100644 idea/testData/quickfix/createFromUsage/createFunction/call/receiverWithSmartCast.kt create mode 100644 idea/testData/quickfix/createFromUsage/createFunction/call/receiverWithSmartCast.kt.after diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/typeUtils.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/typeUtils.kt index 9bdaed1257c..3aee8c3b2f6 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/typeUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/typeUtils.kt @@ -164,7 +164,7 @@ fun KtExpression.guessTypes( // if we know the actual type of the expression val theType1 = context.getType(this) if (theType1 != null && isAcceptable(theType1)) { - return getDataFlowAwareTypes(this, context).toTypedArray() + return getDataFlowAwareTypes(this, context, theType1).toTypedArray() } } @@ -341,8 +341,12 @@ private fun TypePredicate.getRepresentativeTypes(): Set { } } -fun getDataFlowAwareTypes(expression: KtExpression, bindingContext: BindingContext = expression.analyze()): List { - val originalType = bindingContext.getType(expression) ?: return emptyList() +fun getDataFlowAwareTypes( + expression: KtExpression, + bindingContext: BindingContext = expression.analyze(), + originalType: KotlinType? = bindingContext.getType(expression) +): Collection { + if (originalType == null) return emptyList() val dataFlowInfo = bindingContext.getDataFlowInfoAfter(expression) val dataFlowValueFactory = expression.getResolutionFacade().frontendService() val dataFlowValue = dataFlowValueFactory.createDataFlowValue( diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableFromCallActionFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableFromCallActionFactory.kt index 38bbb047f46..73f6839cb31 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableFromCallActionFactory.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableFromCallActionFactory.kt @@ -129,7 +129,13 @@ sealed class CreateCallableFromCallActionFactory( if (javaClass == null || !javaClass.canRefactor()) return null TypeInfo.StaticContextRequired(TypeInfo(javaClassifier.defaultType, Variance.IN_VARIANCE)) } - is ReceiverValue -> TypeInfo(receiver.type, Variance.IN_VARIANCE) + is ReceiverValue -> { + val originalType = receiver.type + val finalType = if (receiver is ExpressionReceiver) { + getDataFlowAwareTypes(receiver.expression, context, originalType).firstOrNull() ?: originalType + } else originalType + TypeInfo(finalType, Variance.IN_VARIANCE) + } else -> throw AssertionError("Unexpected receiver: $receiver") } } diff --git a/idea/testData/quickfix/createFromUsage/createFunction/call/receiverWithSmartCast.kt b/idea/testData/quickfix/createFromUsage/createFunction/call/receiverWithSmartCast.kt new file mode 100644 index 00000000000..cc5c450fc00 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/call/receiverWithSmartCast.kt @@ -0,0 +1,10 @@ +// "Create member function 'SomeObj.doSomething'" "true" +class SomeObj { } + +fun doSomething(p: Any): List{ + if (p is SomeObj){ + p.doSomething() + + } + return emptyList() +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createFunction/call/receiverWithSmartCast.kt.after b/idea/testData/quickfix/createFromUsage/createFunction/call/receiverWithSmartCast.kt.after new file mode 100644 index 00000000000..c04e19e8b26 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/call/receiverWithSmartCast.kt.after @@ -0,0 +1,14 @@ +// "Create member function 'SomeObj.doSomething'" "true" +class SomeObj { + fun doSomething() { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } +} + +fun doSomething(p: Any): List{ + if (p is SomeObj){ + p.doSomething() + + } + return emptyList() +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java index d84e94923af..dfe3e66cb84 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -3114,6 +3114,11 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { runTest("idea/testData/quickfix/createFromUsage/createFunction/call/qualifiedCallInStringTemplateRuntime.kt"); } + @TestMetadata("receiverWithSmartCast.kt") + public void testReceiverWithSmartCast() throws Exception { + runTest("idea/testData/quickfix/createFromUsage/createFunction/call/receiverWithSmartCast.kt"); + } + @TestMetadata("refInImport.kt") public void testRefInImport() throws Exception { runTest("idea/testData/quickfix/createFromUsage/createFunction/call/refInImport.kt");