From 30f98e67302fabeaff5a413cb402ca1ecef8848f Mon Sep 17 00:00:00 2001 From: Dmitry Gridin Date: Fri, 26 Jun 2020 12:34:34 +0700 Subject: [PATCH] Inline refactoring: shouldn't lose return type information #KT-26705 Fixed --- .../inline/KotlinInlineFunctionHandler.kt | 10 +++++--- .../idea/refactoring/inline/inlineUtils.kt | 17 ++++++------- .../ImplicitGenericTypeInReturn.kt | 9 +++++++ .../ImplicitGenericTypeInReturn.kt.after | 6 +++++ ...cTypeInReturnWithInvalidStatementBefore.kt | 10 ++++++++ ...nReturnWithInvalidStatementBefore.kt.after | 7 ++++++ ...ricTypeInReturnWithOtherStatementBefore.kt | 10 ++++++++ ...eInReturnWithOtherStatementBefore.kt.after | 7 ++++++ .../ImplicitUnitGenericTypeInReturn.kt | 9 +++++++ .../ImplicitUnitGenericTypeInReturn.kt.after | 6 +++++ .../inline/function/returnAtEnd/Kt26705.kt | 11 ++++++++ .../function/returnAtEnd/Kt26705.kt.after | 8 ++++++ .../inline/InlineTestGenerated.java | 25 +++++++++++++++++++ 13 files changed, 121 insertions(+), 14 deletions(-) create mode 100644 idea/testData/refactoring/inline/function/returnAtEnd/ImplicitGenericTypeInReturn.kt create mode 100644 idea/testData/refactoring/inline/function/returnAtEnd/ImplicitGenericTypeInReturn.kt.after create mode 100644 idea/testData/refactoring/inline/function/returnAtEnd/ImplicitGenericTypeInReturnWithInvalidStatementBefore.kt create mode 100644 idea/testData/refactoring/inline/function/returnAtEnd/ImplicitGenericTypeInReturnWithInvalidStatementBefore.kt.after create mode 100644 idea/testData/refactoring/inline/function/returnAtEnd/ImplicitGenericTypeInReturnWithOtherStatementBefore.kt create mode 100644 idea/testData/refactoring/inline/function/returnAtEnd/ImplicitGenericTypeInReturnWithOtherStatementBefore.kt.after create mode 100644 idea/testData/refactoring/inline/function/returnAtEnd/ImplicitUnitGenericTypeInReturn.kt create mode 100644 idea/testData/refactoring/inline/function/returnAtEnd/ImplicitUnitGenericTypeInReturn.kt.after create mode 100644 idea/testData/refactoring/inline/function/returnAtEnd/Kt26705.kt create mode 100644 idea/testData/refactoring/inline/function/returnAtEnd/Kt26705.kt.after diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/KotlinInlineFunctionHandler.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/KotlinInlineFunctionHandler.kt index 33757d58b76..7c82da3014f 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/KotlinInlineFunctionHandler.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/KotlinInlineFunctionHandler.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ @@ -15,16 +15,17 @@ import com.intellij.refactoring.RefactoringBundle import com.intellij.refactoring.util.CommonRefactoringUtil import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor +import org.jetbrains.kotlin.idea.KotlinBundle import org.jetbrains.kotlin.idea.KotlinLanguage import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithContent import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor import org.jetbrains.kotlin.idea.codeInliner.CallableUsageReplacementStrategy -import org.jetbrains.kotlin.idea.KotlinBundle import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtNamedFunction import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall +import org.jetbrains.kotlin.types.typeUtil.isUnit class KotlinInlineFunctionHandler : InlineActionHandler() { override fun isEnabledForLanguage(language: Language) = language == KotlinLanguage.INSTANCE @@ -47,10 +48,11 @@ class KotlinInlineFunctionHandler : InlineActionHandler() { } val descriptor = element.unsafeResolveToDescriptor() as SimpleFunctionDescriptor + val returnType = descriptor.returnType val codeToInline = buildCodeToInline( element, - descriptor.returnType, - element.hasDeclaredReturnType(), + returnType, + element.hasDeclaredReturnType() || (element.hasBlockBody() && returnType?.isUnit() == true), element.bodyExpression!!, element.hasBlockBody(), editor diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/inlineUtils.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/inlineUtils.kt index ea9c273af2a..9d9ce9c7446 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/inlineUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/inlineUtils.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ @@ -119,20 +119,17 @@ internal fun buildCodeToInline( isBlockBody: Boolean, editor: Editor? ): CodeToInline? { - val bodyCopy = bodyOrInitializer.copied() - - val expectedType = if (!isBlockBody && isReturnTypeExplicit) - returnType ?: TypeUtils.NO_EXPECTED_TYPE - else - TypeUtils.NO_EXPECTED_TYPE - val scope by lazy { bodyOrInitializer.getResolutionScope() } fun analyzeExpressionInContext(expression: KtExpression): BindingContext = expression.analyzeInContext( scope = scope, contextExpression = bodyOrInitializer, - expectedType = expectedType + expectedType = if (isReturnTypeExplicit && (!isBlockBody || expression.parent is KtReturnExpression)) + returnType ?: TypeUtils.NO_EXPECTED_TYPE + else + TypeUtils.NO_EXPECTED_TYPE ) + val bodyCopy = bodyOrInitializer.copied() val descriptor = declaration.unsafeResolveToDescriptor() val builder = CodeToInlineBuilder(descriptor as CallableDescriptor, declaration.getResolutionFacade()) if (isBlockBody) { @@ -166,7 +163,7 @@ internal fun buildCodeToInline( } return builder.prepareCodeToInline( - lastReturn?.returnedExpression?.copied(), + lastReturn?.returnedExpression, statements.dropLast(returnStatements.size), ::analyzeExpressionInContext, reformat = true ) } else { diff --git a/idea/testData/refactoring/inline/function/returnAtEnd/ImplicitGenericTypeInReturn.kt b/idea/testData/refactoring/inline/function/returnAtEnd/ImplicitGenericTypeInReturn.kt new file mode 100644 index 00000000000..8c4c4e4d5ab --- /dev/null +++ b/idea/testData/refactoring/inline/function/returnAtEnd/ImplicitGenericTypeInReturn.kt @@ -0,0 +1,9 @@ +fun buildList(size: Int) = listOf() + +fun f() { + buildMyList(5) +} + +private fun buildMyList(count: Int): List { + return buildList(size = count) +} diff --git a/idea/testData/refactoring/inline/function/returnAtEnd/ImplicitGenericTypeInReturn.kt.after b/idea/testData/refactoring/inline/function/returnAtEnd/ImplicitGenericTypeInReturn.kt.after new file mode 100644 index 00000000000..5a9f8240ac8 --- /dev/null +++ b/idea/testData/refactoring/inline/function/returnAtEnd/ImplicitGenericTypeInReturn.kt.after @@ -0,0 +1,6 @@ +fun buildList(size: Int) = listOf() + +fun f() { + buildList(size = 5) +} + diff --git a/idea/testData/refactoring/inline/function/returnAtEnd/ImplicitGenericTypeInReturnWithInvalidStatementBefore.kt b/idea/testData/refactoring/inline/function/returnAtEnd/ImplicitGenericTypeInReturnWithInvalidStatementBefore.kt new file mode 100644 index 00000000000..dfe168d086f --- /dev/null +++ b/idea/testData/refactoring/inline/function/returnAtEnd/ImplicitGenericTypeInReturnWithInvalidStatementBefore.kt @@ -0,0 +1,10 @@ +fun buildList(size: Int) = listOf() + +fun f() { + buildMyList(5) +} + +private fun buildMyList(count: Int): List { + buildList(size = count) + return buildList(size = count) +} diff --git a/idea/testData/refactoring/inline/function/returnAtEnd/ImplicitGenericTypeInReturnWithInvalidStatementBefore.kt.after b/idea/testData/refactoring/inline/function/returnAtEnd/ImplicitGenericTypeInReturnWithInvalidStatementBefore.kt.after new file mode 100644 index 00000000000..d3e578324b0 --- /dev/null +++ b/idea/testData/refactoring/inline/function/returnAtEnd/ImplicitGenericTypeInReturnWithInvalidStatementBefore.kt.after @@ -0,0 +1,7 @@ +fun buildList(size: Int) = listOf() + +fun f() { + buildList(size = 5) + buildList(size = 5) +} + diff --git a/idea/testData/refactoring/inline/function/returnAtEnd/ImplicitGenericTypeInReturnWithOtherStatementBefore.kt b/idea/testData/refactoring/inline/function/returnAtEnd/ImplicitGenericTypeInReturnWithOtherStatementBefore.kt new file mode 100644 index 00000000000..20cb8d394c5 --- /dev/null +++ b/idea/testData/refactoring/inline/function/returnAtEnd/ImplicitGenericTypeInReturnWithOtherStatementBefore.kt @@ -0,0 +1,10 @@ +fun buildList(size: Int) = listOf() + +fun f() { + buildMyList(5) +} + +private fun buildMyList(count: Int): List { + val b: List = buildList(size = count) + return buildList(size = count) +} diff --git a/idea/testData/refactoring/inline/function/returnAtEnd/ImplicitGenericTypeInReturnWithOtherStatementBefore.kt.after b/idea/testData/refactoring/inline/function/returnAtEnd/ImplicitGenericTypeInReturnWithOtherStatementBefore.kt.after new file mode 100644 index 00000000000..3b8aa08f762 --- /dev/null +++ b/idea/testData/refactoring/inline/function/returnAtEnd/ImplicitGenericTypeInReturnWithOtherStatementBefore.kt.after @@ -0,0 +1,7 @@ +fun buildList(size: Int) = listOf() + +fun f() { + val b: List = buildList(size = 5) + buildList(size = 5) +} + diff --git a/idea/testData/refactoring/inline/function/returnAtEnd/ImplicitUnitGenericTypeInReturn.kt b/idea/testData/refactoring/inline/function/returnAtEnd/ImplicitUnitGenericTypeInReturn.kt new file mode 100644 index 00000000000..465dc40ddf0 --- /dev/null +++ b/idea/testData/refactoring/inline/function/returnAtEnd/ImplicitUnitGenericTypeInReturn.kt @@ -0,0 +1,9 @@ +fun buildType(size: Int): T = TODO() + +fun f() { + buildMyList(5) +} + +private fun buildMyList(count: Int) { + return buildType(size = count) +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/function/returnAtEnd/ImplicitUnitGenericTypeInReturn.kt.after b/idea/testData/refactoring/inline/function/returnAtEnd/ImplicitUnitGenericTypeInReturn.kt.after new file mode 100644 index 00000000000..43372dec9b9 --- /dev/null +++ b/idea/testData/refactoring/inline/function/returnAtEnd/ImplicitUnitGenericTypeInReturn.kt.after @@ -0,0 +1,6 @@ +fun buildType(size: Int): T = TODO() + +fun f() { + buildType(size = 5) +} + diff --git a/idea/testData/refactoring/inline/function/returnAtEnd/Kt26705.kt b/idea/testData/refactoring/inline/function/returnAtEnd/Kt26705.kt new file mode 100644 index 00000000000..c94eded478b --- /dev/null +++ b/idea/testData/refactoring/inline/function/returnAtEnd/Kt26705.kt @@ -0,0 +1,11 @@ +fun buildList(size: Int) = listOf() + +class Test { + fun f() { + val list = buildMyList(5) + } + + private fun buildMyList(count: Int): List { + return buildList(size = count) + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/function/returnAtEnd/Kt26705.kt.after b/idea/testData/refactoring/inline/function/returnAtEnd/Kt26705.kt.after new file mode 100644 index 00000000000..224ec0b4a9b --- /dev/null +++ b/idea/testData/refactoring/inline/function/returnAtEnd/Kt26705.kt.after @@ -0,0 +1,8 @@ +fun buildList(size: Int) = listOf() + +class Test { + fun f() { + val list = buildList(size = 5) + } + +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/inline/InlineTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/refactoring/inline/InlineTestGenerated.java index c1d04b425b9..6f002ae76a6 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/inline/InlineTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/inline/InlineTestGenerated.java @@ -463,6 +463,26 @@ public class InlineTestGenerated extends AbstractInlineTest { runTest("idea/testData/refactoring/inline/function/returnAtEnd/GenericTypeArgument2.kt"); } + @TestMetadata("ImplicitGenericTypeInReturn.kt") + public void testImplicitGenericTypeInReturn() throws Exception { + runTest("idea/testData/refactoring/inline/function/returnAtEnd/ImplicitGenericTypeInReturn.kt"); + } + + @TestMetadata("ImplicitGenericTypeInReturnWithInvalidStatementBefore.kt") + public void testImplicitGenericTypeInReturnWithInvalidStatementBefore() throws Exception { + runTest("idea/testData/refactoring/inline/function/returnAtEnd/ImplicitGenericTypeInReturnWithInvalidStatementBefore.kt"); + } + + @TestMetadata("ImplicitGenericTypeInReturnWithOtherStatementBefore.kt") + public void testImplicitGenericTypeInReturnWithOtherStatementBefore() throws Exception { + runTest("idea/testData/refactoring/inline/function/returnAtEnd/ImplicitGenericTypeInReturnWithOtherStatementBefore.kt"); + } + + @TestMetadata("ImplicitUnitGenericTypeInReturn.kt") + public void testImplicitUnitGenericTypeInReturn() throws Exception { + runTest("idea/testData/refactoring/inline/function/returnAtEnd/ImplicitUnitGenericTypeInReturn.kt"); + } + @TestMetadata("InnerFunction.kt") public void testInnerFunction() throws Exception { runTest("idea/testData/refactoring/inline/function/returnAtEnd/InnerFunction.kt"); @@ -473,6 +493,11 @@ public class InlineTestGenerated extends AbstractInlineTest { runTest("idea/testData/refactoring/inline/function/returnAtEnd/InnerFunction2.kt"); } + @TestMetadata("Kt26705.kt") + public void testKt26705() throws Exception { + runTest("idea/testData/refactoring/inline/function/returnAtEnd/Kt26705.kt"); + } + @TestMetadata("MultipleStatements.kt") public void testMultipleStatements() throws Exception { runTest("idea/testData/refactoring/inline/function/returnAtEnd/MultipleStatements.kt");