Checking for unsupported return's

This commit is contained in:
Valentin Kipyatkov
2016-10-25 18:38:32 +03:00
parent 779ed32a4b
commit 85e1e53354
6 changed files with 79 additions and 2 deletions
@@ -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<KtReturnExpression> {
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)
}
@@ -0,0 +1,14 @@
// ERROR: Cannot perform refactoring.\nInline Function is not supported for functions with multiple return statements.
fun <caret>f(p1: Int, p2: Int): Int {
if (p1 > 0) {
return p2
}
else {
return -1
}
}
fun g() {
f(1, 2)
}
@@ -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 <caret>f(p1: Int, p2: Int): Int {
while (true) {
if (x() > p1) return@f p2
}
}
fun x(): Int = TODO()
fun g() {
f(1, 2)
}
@@ -0,0 +1,10 @@
fun <caret>f(list: List<String>): Boolean {
return list.any {
if (it.isEmpty()) return@any false
it.length < 10
}
}
fun g() {
println(f(listOf("a", "b")))
}
@@ -0,0 +1,6 @@
fun g() {
println(listOf("a", "b").any {
if (it.isEmpty()) return@any false
it.length < 10
})
}
@@ -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");