"Lift return / assignment": don't highlight if other returns available

This commit is contained in:
Mikhail Glukhikh
2017-07-06 15:54:07 +03:00
parent 691b733c55
commit 1ddaee5b4a
5 changed files with 62 additions and 28 deletions
@@ -22,10 +22,8 @@ import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.BranchedFoldingUtils
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isIfBranch
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.lineCount
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtIfExpression
import org.jetbrains.kotlin.psi.KtVisitorVoid
import org.jetbrains.kotlin.psi.KtWhenExpression
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
class LiftReturnOrAssignmentInspection : AbstractKotlinInspection() {
@@ -36,12 +34,13 @@ class LiftReturnOrAssignmentInspection : AbstractKotlinInspection() {
if (expression.lineCount() > LINES_LIMIT) return
if (expression.isIfBranch()) return
val returnNumber = BranchedFoldingUtils.getFoldableReturnNumber(expression)
if (returnNumber > 0) {
val foldableReturns = BranchedFoldingUtils.getFoldableReturns(expression)
if (foldableReturns?.isNotEmpty() == true) {
val hasOtherReturns = expression.anyDescendantOfType<KtReturnExpression> { it !in foldableReturns }
holder.registerProblem(
keyword,
"Return can be lifted out of '${keyword.text}'",
if (returnNumber > 1) ProblemHighlightType.GENERIC_ERROR_OR_WARNING
if (!hasOtherReturns && foldableReturns.size > 1) ProblemHighlightType.GENERIC_ERROR_OR_WARNING
else ProblemHighlightType.INFORMATION,
LiftReturnOutFix(keyword.text)
)
@@ -95,44 +95,47 @@ object BranchedFoldingUtils {
return assignments.size
}
private fun getFoldableReturnNumber(branches: List<KtExpression?>) =
branches.fold(0) { prevNumber, branch ->
when {
prevNumber == -1 -> -1
getFoldableBranchedReturn(branch) != null -> prevNumber + 1
else -> {
val currNumber = getFoldableReturnNumber(branch?.lastBlockStatementOrThis())
if (currNumber == -1) -1 else prevNumber + currNumber
}
private fun getFoldableReturns(branches: List<KtExpression?>): List<KtReturnExpression>? =
branches.fold<KtExpression?, MutableList<KtReturnExpression>?>(mutableListOf()) { prevList, branch ->
if (prevList == null) return@fold null
val foldableBranchedReturn = getFoldableBranchedReturn(branch)
if (foldableBranchedReturn != null) {
prevList.add(foldableBranchedReturn)
}
else {
val currReturns = getFoldableReturns(branch?.lastBlockStatementOrThis()) ?: return@fold null
prevList += currReturns
}
prevList
}
internal fun getFoldableReturnNumber(expression: KtExpression?): Int = when (expression) {
internal fun getFoldableReturns(expression: KtExpression?): List<KtReturnExpression>? = when (expression) {
is KtWhenExpression -> {
val entries = expression.entries
when {
!KtPsiUtil.checkWhenExpressionHasSingleElse(expression) -> -1
entries.isEmpty() -> -1
else -> getFoldableReturnNumber(entries.map { it.expression })
!KtPsiUtil.checkWhenExpressionHasSingleElse(expression) -> null
entries.isEmpty() -> null
else -> getFoldableReturns(entries.map { it.expression })
}
}
is KtIfExpression -> {
val branches = expression.branches
when {
branches.isEmpty() -> -1
branches.lastOrNull()?.getStrictParentOfType<KtIfExpression>()?.`else` == null -> -1
else -> getFoldableReturnNumber(branches)
branches.isEmpty() -> null
branches.lastOrNull()?.getStrictParentOfType<KtIfExpression>()?.`else` == null -> null
else -> getFoldableReturns(branches)
}
}
is KtCallExpression -> {
if (expression.analyze().getType(expression)?.isNothing() == true) 0 else -1
if (expression.analyze().getType(expression)?.isNothing() == true) emptyList() else null
}
is KtBreakExpression, is KtContinueExpression, is KtThrowExpression -> 0
else -> -1
is KtBreakExpression, is KtContinueExpression, is KtThrowExpression -> emptyList()
else -> null
}
fun canFoldToReturn(expression: KtExpression?): Boolean =
getFoldableReturnNumber(expression) > 0
private fun getFoldableReturnNumber(expression: KtExpression?) = getFoldableReturns(expression)?.size ?: -1
fun canFoldToReturn(expression: KtExpression?): Boolean = getFoldableReturnNumber(expression) > 0
fun foldToAssignment(expression: KtExpression) {
var lhs: KtExpression? = null
@@ -0,0 +1,13 @@
// HIGHLIGHT: INFORMATION
fun test(n: Int, arg: String?): String {
<caret>when (n) {
1 -> {
if (arg == null) return ""
return "** $arg"
}
else -> {
return "Strange"
}
}
}
@@ -0,0 +1,13 @@
// HIGHLIGHT: INFORMATION
fun test(n: Int, arg: String?): String {
return when (n) {
1 -> {
if (arg == null) return ""
"** $arg"
}
else -> {
"Strange"
}
}
}
@@ -643,6 +643,12 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
doTest(fileName);
}
@TestMetadata("otherReturns.kt")
public void testOtherReturns() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/liftOut/whenToReturn/otherReturns.kt");
doTest(fileName);
}
@TestMetadata("simpleWhen.kt")
public void testSimpleWhen() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/liftOut/whenToReturn/simpleWhen.kt");