diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractableAnalysisUtil.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractableAnalysisUtil.kt index 7e4867895f1..75fb0bd79bf 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractableAnalysisUtil.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractableAnalysisUtil.kt @@ -69,8 +69,10 @@ import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsStatement import org.jetbrains.kotlin.resolve.calls.CallTransformer import org.jetbrains.kotlin.resolve.calls.callUtil.getCalleeExpressionIfAny +import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory import org.jetbrains.kotlin.resolve.scopes.JetScope import org.jetbrains.kotlin.resolve.scopes.JetScopeUtils +import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue import org.jetbrains.kotlin.resolve.scopes.receivers.ThisReceiver import org.jetbrains.kotlin.types.* @@ -642,13 +644,27 @@ private fun ExtractionData.inferParametersInfo( val extractParameter = extractThis || extractLocalVar if (extractParameter) { - val parameterType = when { - receiverToExtract.exists() -> receiverToExtract.getType() - else -> bindingContext[BindingContext.SMARTCAST, originalRef] - ?: bindingContext[BindingContext.EXPRESSION_TYPE, originalRef] - ?: DEFAULT_PARAMETER_TYPE + val parameterExpression = when { + receiverToExtract is ExpressionReceiver -> receiverToExtract.getExpression() + receiverToExtract.exists() -> null + else -> (originalRef.getParent() as? JetThisExpression) ?: originalRef } + val parameterType = when { + parameterExpression != null -> bindingContext[BindingContext.SMARTCAST, parameterExpression] + ?: bindingContext[BindingContext.EXPRESSION_TYPE, parameterExpression] + ?: if (receiverToExtract.exists()) receiverToExtract.getType() else null + receiverToExtract is ThisReceiver -> { + val calleeExpression = resolvedCall!!.getCall().getCalleeExpression() + bindingContext[BindingContext.EXPRESSION_DATA_FLOW_INFO, calleeExpression]?.let { dataFlowInfo -> + val possibleTypes = dataFlowInfo.getPossibleTypes(DataFlowValueFactory.createDataFlowValue(receiverToExtract)) + CommonSupertypes.commonSupertype(possibleTypes) + } ?: receiverToExtract.getType() + } + receiverToExtract.exists() -> receiverToExtract.getType() + else -> null + } ?: DEFAULT_PARAMETER_TYPE + val parameter = extractedDescriptorToParameter.getOrPut(descriptorToExtract) { val argumentText = if (hasThisReceiver && extractThis) { diff --git a/idea/testData/refactoring/extractFunction/parameters/extractThis/explicitThisWithSmartCast.kt b/idea/testData/refactoring/extractFunction/parameters/extractThis/explicitThisWithSmartCast.kt new file mode 100644 index 00000000000..d56323fdfc9 --- /dev/null +++ b/idea/testData/refactoring/extractFunction/parameters/extractThis/explicitThisWithSmartCast.kt @@ -0,0 +1,17 @@ +// SUGGESTED_NAMES: i, getB +// PARAM_DESCRIPTOR: internal fun A.ext(): kotlin.Unit defined in root package +// PARAM_TYPES: B +fun main(args: Array) { + val a: A = B() + a.ext() +} + +fun A.ext() { + if (this !is B) return + val b = this.foo() +} + +open class A +class B: A() { + fun foo() = 1 +} \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/parameters/extractThis/explicitThisWithSmartCast.kt.after b/idea/testData/refactoring/extractFunction/parameters/extractThis/explicitThisWithSmartCast.kt.after new file mode 100644 index 00000000000..118bd7aed9c --- /dev/null +++ b/idea/testData/refactoring/extractFunction/parameters/extractThis/explicitThisWithSmartCast.kt.after @@ -0,0 +1,19 @@ +// SUGGESTED_NAMES: i, getB +// PARAM_DESCRIPTOR: internal fun A.ext(): kotlin.Unit defined in root package +// PARAM_TYPES: B +fun main(args: Array) { + val a: A = B() + a.ext() +} + +fun A.ext() { + if (this !is B) return + val b = i() +} + +private fun B.i() = this.foo() + +open class A +class B: A() { + fun foo() = 1 +} \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/parameters/extractThis/implicitThisWithSmartCast.kt b/idea/testData/refactoring/extractFunction/parameters/extractThis/implicitThisWithSmartCast.kt new file mode 100644 index 00000000000..e53d99efebf --- /dev/null +++ b/idea/testData/refactoring/extractFunction/parameters/extractThis/implicitThisWithSmartCast.kt @@ -0,0 +1,17 @@ +// SUGGESTED_NAMES: i, getB +// PARAM_DESCRIPTOR: internal fun A.ext(): kotlin.Unit defined in root package +// PARAM_TYPES: B +fun main(args: Array) { + val a: A = B() + a.ext() +} + +fun A.ext() { + if (this !is B) return + val b = foo() +} + +open class A +class B: A() { + fun foo() = 1 +} \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/parameters/extractThis/implicitThisWithSmartCast.kt.after b/idea/testData/refactoring/extractFunction/parameters/extractThis/implicitThisWithSmartCast.kt.after new file mode 100644 index 00000000000..77db121529c --- /dev/null +++ b/idea/testData/refactoring/extractFunction/parameters/extractThis/implicitThisWithSmartCast.kt.after @@ -0,0 +1,19 @@ +// SUGGESTED_NAMES: i, getB +// PARAM_DESCRIPTOR: internal fun A.ext(): kotlin.Unit defined in root package +// PARAM_TYPES: B +fun main(args: Array) { + val a: A = B() + a.ext() +} + +fun A.ext() { + if (this !is B) return + val b = i() +} + +private fun B.i() = foo() + +open class A +class B: A() { + fun foo() = 1 +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/JetExtractionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/JetExtractionTestGenerated.java index 0044f3c5b6c..d81ab796f5a 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/JetExtractionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/JetExtractionTestGenerated.java @@ -1643,6 +1643,12 @@ public class JetExtractionTestGenerated extends AbstractJetExtractionTest { doExtractFunctionTest(fileName); } + @TestMetadata("explicitThisWithSmartCast.kt") + public void testExplicitThisWithSmartCast() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/parameters/extractThis/explicitThisWithSmartCast.kt"); + doExtractFunctionTest(fileName); + } + @TestMetadata("implicitAndExplicitLabeledThisInMember.kt") public void testImplicitAndExplicitLabeledThisInMember() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/parameters/extractThis/implicitAndExplicitLabeledThisInMember.kt"); @@ -1667,6 +1673,12 @@ public class JetExtractionTestGenerated extends AbstractJetExtractionTest { doExtractFunctionTest(fileName); } + @TestMetadata("implicitThisWithSmartCast.kt") + public void testImplicitThisWithSmartCast() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/parameters/extractThis/implicitThisWithSmartCast.kt"); + doExtractFunctionTest(fileName); + } + @TestMetadata("qualifiedThisAsArgument.kt") public void testQualifiedThisAsArgument() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/parameters/extractThis/qualifiedThisAsArgument.kt");