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 b1234185d4c..384701b67ce 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/KotlinInlineFunctionHandler.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/KotlinInlineFunctionHandler.kt @@ -23,19 +23,21 @@ import com.intellij.openapi.project.Project import com.intellij.psi.PsiElement import com.intellij.psi.search.GlobalSearchScope import com.intellij.refactoring.RefactoringBundle +import com.intellij.refactoring.util.CommonRefactoringUtil import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor import org.jetbrains.kotlin.idea.KotlinLanguage import org.jetbrains.kotlin.idea.analysis.analyzeInContext import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor -import org.jetbrains.kotlin.idea.core.copied import org.jetbrains.kotlin.idea.codeInliner.CallableUsageReplacementStrategy import org.jetbrains.kotlin.idea.codeInliner.CodeToInlineBuilder import org.jetbrains.kotlin.idea.codeInliner.replaceUsagesInWholeProject +import org.jetbrains.kotlin.idea.core.copied import org.jetbrains.kotlin.idea.util.getResolutionScope import org.jetbrains.kotlin.psi.KtBlockExpression import org.jetbrains.kotlin.psi.KtNamedFunction import org.jetbrains.kotlin.psi.KtReturnExpression +import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.types.TypeUtils @@ -71,8 +73,22 @@ class KotlinInlineFunctionHandler: InlineActionHandler() { val replacement = if (element.hasBlockBody()) { bodyCopy as KtBlockExpression val statements = bodyCopy.statements - //TODO: check no other return's! + + val returnStatements = bodyCopy.collectDescendantsOfType { + it.getLabelName().let { it == null || it == element.name } + } + val lastReturn = statements.lastOrNull() as? KtReturnExpression + if (returnStatements.any { it != lastReturn }) { + val message = RefactoringBundle.getCannotRefactorMessage( + if (returnStatements.size > 1) + "Inline Function is not supported for functions with multiple return statements." + else + "Inline Function is not supported for functions with return statements not at the end of the body." + ) + CommonRefactoringUtil.showErrorHint(project, editor, message, "Inline Function", null) + } + if (lastReturn != null) { replacementBuilder.prepareCodeToInline(lastReturn.returnedExpression, statements.dropLast(1), ::analyzeBodyCopy) } diff --git a/idea/testData/refactoring/inline/function/MultipleReturns.kt b/idea/testData/refactoring/inline/function/MultipleReturns.kt new file mode 100644 index 00000000000..b21d8c19e35 --- /dev/null +++ b/idea/testData/refactoring/inline/function/MultipleReturns.kt @@ -0,0 +1,14 @@ +// ERROR: Cannot perform refactoring.\nInline Function is not supported for functions with multiple return statements. + +fun f(p1: Int, p2: Int): Int { + if (p1 > 0) { + return p2 + } + else { + return -1 + } +} + +fun g() { + f(1, 2) +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/function/ReturnNotInTheEnd.kt b/idea/testData/refactoring/inline/function/ReturnNotInTheEnd.kt new file mode 100644 index 00000000000..13dfd1b4564 --- /dev/null +++ b/idea/testData/refactoring/inline/function/ReturnNotInTheEnd.kt @@ -0,0 +1,13 @@ +// ERROR: Cannot perform refactoring.\nInline Function is not supported for functions with return statements not at the end of the body. + +fun f(p1: Int, p2: Int): Int { + while (true) { + if (x() > p1) return@f p2 + } +} + +fun x(): Int = TODO() + +fun g() { + f(1, 2) +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/function/returnAtEnd/ReturnFromLambda.kt b/idea/testData/refactoring/inline/function/returnAtEnd/ReturnFromLambda.kt new file mode 100644 index 00000000000..680ce3ce4a5 --- /dev/null +++ b/idea/testData/refactoring/inline/function/returnAtEnd/ReturnFromLambda.kt @@ -0,0 +1,10 @@ +fun f(list: List): Boolean { + return list.any { + if (it.isEmpty()) return@any false + it.length < 10 + } +} + +fun g() { + println(f(listOf("a", "b"))) +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/function/returnAtEnd/ReturnFromLambda.kt.after b/idea/testData/refactoring/inline/function/returnAtEnd/ReturnFromLambda.kt.after new file mode 100644 index 00000000000..7edca71c0e8 --- /dev/null +++ b/idea/testData/refactoring/inline/function/returnAtEnd/ReturnFromLambda.kt.after @@ -0,0 +1,6 @@ +fun g() { + println(listOf("a", "b").any { + if (it.isEmpty()) return@any false + it.length < 10 + }) +} 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 ca24efc2efa..19e6e43c480 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/inline/InlineTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/inline/InlineTestGenerated.java @@ -49,6 +49,18 @@ public class InlineTestGenerated extends AbstractInlineTest { doTest(fileName); } + @TestMetadata("MultipleReturns.kt") + public void testMultipleReturns() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/function/MultipleReturns.kt"); + doTest(fileName); + } + + @TestMetadata("ReturnNotInTheEnd.kt") + public void testReturnNotInTheEnd() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/function/ReturnNotInTheEnd.kt"); + doTest(fileName); + } + @TestMetadata("UnitReturnType.kt") public void testUnitReturnType() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/function/UnitReturnType.kt"); @@ -150,6 +162,12 @@ public class InlineTestGenerated extends AbstractInlineTest { doTest(fileName); } + @TestMetadata("ReturnFromLambda.kt") + public void testReturnFromLambda() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/function/returnAtEnd/ReturnFromLambda.kt"); + doTest(fileName); + } + @TestMetadata("SafeCall.kt") public void testSafeCall() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/function/returnAtEnd/SafeCall.kt");