Lift return out: do not report for zero returns

Also, do not highlight (but suggest fix) for exactly one return
Related to KT-14900
This commit is contained in:
Mikhail Glukhikh
2017-07-05 14:56:24 +03:00
parent 2deef48b84
commit 1010ecca8c
8 changed files with 72 additions and 15 deletions
@@ -31,15 +31,18 @@ class LiftReturnOrAssignmentInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession) =
object : KtVisitorVoid() {
private fun visitIfOrWhen(expression: KtExpression, keyword: PsiElement) {
if (BranchedFoldingUtils.canFoldToReturn(expression)) {
val returnNumber = BranchedFoldingUtils.getFoldableReturnNumber(expression)
if (returnNumber > 0) {
holder.registerProblem(
keyword,
"Return can be lifted out of '${keyword.text}'",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
if (returnNumber > 1) ProblemHighlightType.GENERIC_ERROR_OR_WARNING
else ProblemHighlightType.INFORMATION,
LiftReturnOutFix(keyword.text)
)
return
}
else if (BranchedFoldingUtils.canFoldToAssignment(expression)) {
if (BranchedFoldingUtils.canFoldToAssignment(expression)) {
holder.registerProblem(
keyword,
"Assignment can be lifted out of '${keyword.text}'",
@@ -82,30 +82,45 @@ object BranchedFoldingUtils {
return assignments.all { BranchedFoldingUtils.checkAssignmentsMatch(it, firstAssignment) }
}
fun canFoldToReturn(expression: KtExpression?): Boolean = when (expression) {
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
}
}
}
internal fun getFoldableReturnNumber(expression: KtExpression?): Int = when (expression) {
is KtWhenExpression -> {
val entries = expression.entries
KtPsiUtil.checkWhenExpressionHasSingleElse(expression) &&
entries.isNotEmpty() &&
entries.all { entry ->
getFoldableBranchedReturn(entry.expression) != null || canFoldToReturn(entry.expression?.lastBlockStatementOrThis())
when {
!KtPsiUtil.checkWhenExpressionHasSingleElse(expression) -> -1
entries.isEmpty() -> -1
else -> getFoldableReturnNumber(entries.map { it.expression })
}
}
is KtIfExpression -> {
val branches = expression.branches
branches.isNotEmpty() &&
(branches.lastOrNull()?.getStrictParentOfType<KtIfExpression>()?.`else` != null) &&
branches.all { branch ->
getFoldableBranchedReturn(branch) != null || canFoldToReturn(branch?.lastBlockStatementOrThis())
when {
branches.isEmpty() -> -1
branches.lastOrNull()?.getStrictParentOfType<KtIfExpression>()?.`else` == null -> -1
else -> getFoldableReturnNumber(branches)
}
}
is KtCallExpression -> {
expression.analyze().getType(expression)?.isNothing() ?: false
if (expression.analyze().getType(expression)?.isNothing() == true) 0 else -1
}
is KtBreakExpression, is KtContinueExpression, is KtThrowExpression -> true
else -> false
is KtBreakExpression, is KtContinueExpression, is KtThrowExpression -> 0
else -> -1
}
fun canFoldToReturn(expression: KtExpression?): Boolean =
getFoldableReturnNumber(expression) > 0
fun foldToAssignment(expression: KtExpression) {
var lhs: KtExpression? = null
var op: String? = null
@@ -1,3 +1,5 @@
// HIGHLIGHT: GENERIC_ERROR_OR_WARNING
fun foo(): Int {
loop@ while (true) {
<caret>when (1) {
@@ -1,3 +1,5 @@
// HIGHLIGHT: GENERIC_ERROR_OR_WARNING
fun foo(): Int {
loop@ while (true) {
return when (1) {
@@ -0,0 +1,8 @@
// HIGHLIGHT: INFORMATION
fun foo(arg: Int): Int {
<caret>when (arg) {
0 -> return 0
else -> throw Exception()
}
}
@@ -0,0 +1,8 @@
// HIGHLIGHT: INFORMATION
fun foo(arg: Int): Int {
return when (arg) {
0 -> 0
else -> throw Exception()
}
}
@@ -0,0 +1,7 @@
// PROBLEM: none
fun foo(): Int {
<caret>when {
else -> throw Exception()
}
}
@@ -636,6 +636,18 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/liftOut/whenToReturn/simpleWhenWithBlocks.kt");
doTest(fileName);
}
@TestMetadata("whenOneReturn.kt")
public void testWhenOneReturn() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/liftOut/whenToReturn/whenOneReturn.kt");
doTest(fileName);
}
@TestMetadata("whenThrowOnly.kt")
public void testWhenThrowOnly() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/liftOut/whenToReturn/whenThrowOnly.kt");
doTest(fileName);
}
}
}