Lift return out: report also on 'return' keywords #KT-27173 Fixed

This commit is contained in:
Toshiaki Kameyama
2018-10-25 06:24:01 +03:00
committed by Mikhail Glukhikh
parent f771ed5dd8
commit 3e6007e3d3
13 changed files with 120 additions and 8 deletions
@@ -24,7 +24,7 @@ import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isElseIf
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.lineCount import org.jetbrains.kotlin.idea.intentions.branchedTransformations.lineCount
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType import org.jetbrains.kotlin.psi.psiUtil.startOffset
class LiftReturnOrAssignmentInspection : AbstractKotlinInspection() { class LiftReturnOrAssignmentInspection : AbstractKotlinInspection() {
@@ -39,25 +39,38 @@ class LiftReturnOrAssignmentInspection : AbstractKotlinInspection() {
val hasOtherReturns = expression.anyDescendantOfType<KtReturnExpression> { it !in foldableReturns } val hasOtherReturns = expression.anyDescendantOfType<KtReturnExpression> { it !in foldableReturns }
val isSerious = !hasOtherReturns && foldableReturns.size > 1 val isSerious = !hasOtherReturns && foldableReturns.size > 1
val verb = if (isSerious) "should" else "can" val verb = if (isSerious) "should" else "can"
val description = "Return $verb be lifted out of '${keyword.text}'"
holder.registerProblemWithoutOfflineInformation( holder.registerProblemWithoutOfflineInformation(
keyword, expression,
"Return $verb be lifted out of '${keyword.text}'", description,
isOnTheFly, isOnTheFly,
if (isSerious) ProblemHighlightType.GENERIC_ERROR_OR_WARNING if (isSerious) ProblemHighlightType.GENERIC_ERROR_OR_WARNING
else ProblemHighlightType.INFORMATION, else ProblemHighlightType.INFORMATION,
keyword.textRange?.shiftRight(-expression.startOffset),
LiftReturnOutFix(keyword.text) LiftReturnOutFix(keyword.text)
) )
foldableReturns.forEach {
holder.registerProblemWithoutOfflineInformation(
expression,
description,
isOnTheFly,
ProblemHighlightType.INFORMATION,
it.returnKeyword.textRange?.shiftRight(-expression.startOffset),
LiftReturnOutFix(keyword.text)
)
}
return return
} }
val assignmentNumber = BranchedFoldingUtils.getFoldableAssignmentNumber(expression) val assignmentNumber = BranchedFoldingUtils.getFoldableAssignmentNumber(expression)
if (assignmentNumber > 0) { if (assignmentNumber > 0) {
val verb = if (assignmentNumber > 1) "should" else "can" val verb = if (assignmentNumber > 1) "should" else "can"
holder.registerProblemWithoutOfflineInformation( holder.registerProblemWithoutOfflineInformation(
keyword, expression,
"Assignment $verb be lifted out of '${keyword.text}'", "Assignment $verb be lifted out of '${keyword.text}'",
isOnTheFly, isOnTheFly,
if (assignmentNumber > 1) ProblemHighlightType.GENERIC_ERROR_OR_WARNING if (assignmentNumber > 1) ProblemHighlightType.GENERIC_ERROR_OR_WARNING
else ProblemHighlightType.INFORMATION, else ProblemHighlightType.INFORMATION,
keyword.textRange?.shiftRight(-expression.startOffset),
LiftAssignmentOutFix(keyword.text) LiftAssignmentOutFix(keyword.text)
) )
} }
@@ -87,7 +100,8 @@ class LiftReturnOrAssignmentInspection : AbstractKotlinInspection() {
override fun getFamilyName() = name override fun getFamilyName() = name
override fun applyFix(project: Project, descriptor: ProblemDescriptor) { override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
BranchedFoldingUtils.foldToReturn(descriptor.psiElement.getParentOfType(true)!!) val replaced = BranchedFoldingUtils.foldToReturn(descriptor.psiElement as KtExpression)
replaced.findExistingEditor()?.caretModel?.moveToOffset(replaced.startOffset)
} }
} }
@@ -97,7 +111,7 @@ class LiftReturnOrAssignmentInspection : AbstractKotlinInspection() {
override fun getFamilyName() = name override fun getFamilyName() = name
override fun applyFix(project: Project, descriptor: ProblemDescriptor) { override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
BranchedFoldingUtils.foldToAssignment(descriptor.psiElement.getParentOfType(true)!!) BranchedFoldingUtils.foldToAssignment(descriptor.psiElement as KtExpression)
} }
} }
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.idea.intentions.branchedTransformations
import org.jetbrains.kotlin.cfg.WhenChecker import org.jetbrains.kotlin.cfg.WhenChecker
import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.core.replaced
import org.jetbrains.kotlin.idea.intentions.branches import org.jetbrains.kotlin.idea.intentions.branches
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
@@ -206,7 +207,7 @@ object BranchedFoldingUtils {
expression.replace(psiFactory.createExpressionByPattern("$0 $1 $2", lhs!!, op!!, expression)) expression.replace(psiFactory.createExpressionByPattern("$0 $1 $2", lhs!!, op!!, expression))
} }
fun foldToReturn(expression: KtExpression) { fun foldToReturn(expression: KtExpression): KtExpression {
fun KtReturnExpression.replaceWithReturned() { fun KtReturnExpression.replaceWithReturned() {
replace(returnedExpression!!) replace(returnedExpression!!)
} }
@@ -226,7 +227,7 @@ object BranchedFoldingUtils {
} }
} }
lift(expression) lift(expression)
expression.replace(KtPsiFactory(expression).createExpressionByPattern("return $0", expression)) return expression.replaced(KtPsiFactory(expression).createExpressionByPattern("return $0", expression))
} }
private fun KtTryExpression.tryBlockAndCatchBodies(): List<KtExpression?> = listOf(tryBlock) + catchClauses.map { it.catchBody } private fun KtTryExpression.tryBlockAndCatchBodies(): List<KtExpression?> = listOf(tryBlock) + catchClauses.map { it.catchBody }
@@ -0,0 +1,7 @@
// HIGHLIGHT: INFORMATION
fun test(n: Int): String {
if (n == 1)
<caret>return "one"
else
return "two"
}
@@ -0,0 +1,7 @@
// HIGHLIGHT: INFORMATION
fun test(n: Int): String {
<caret>return if (n == 1)
"one"
else
"two"
}
@@ -0,0 +1,7 @@
// HIGHLIGHT: INFORMATION
fun test(n: Int): String {
if (n == 1)
return "one"
else
<caret>return "two"
}
@@ -0,0 +1,7 @@
// HIGHLIGHT: INFORMATION
fun test(n: Int): String {
<caret>return if (n == 1)
"one"
else
"two"
}
@@ -0,0 +1,8 @@
// HIGHLIGHT: INFORMATION
fun test(): String {
try {
<caret>return "success"
} catch (e: Exception) {
throw e
}
}
@@ -0,0 +1,8 @@
// HIGHLIGHT: INFORMATION
fun test(): String {
<caret>return try {
"success"
} catch (e: Exception) {
throw e
}
}
@@ -0,0 +1,7 @@
// HIGHLIGHT: INFORMATION
fun test(n: Int): String {
when (n) {
1 -> <caret>return "one"
else -> return "two"
}
}
@@ -0,0 +1,7 @@
// HIGHLIGHT: INFORMATION
fun test(n: Int): String {
<caret>return when (n) {
1 -> "one"
else -> "two"
}
}
@@ -0,0 +1,7 @@
// HIGHLIGHT: INFORMATION
fun test(n: Int): String {
when (n) {
1 -> return "one"
else -> <caret>return "two"
}
}
@@ -0,0 +1,7 @@
// HIGHLIGHT: INFORMATION
fun test(n: Int): String {
<caret>return when (n) {
1 -> "one"
else -> "two"
}
}
@@ -2865,6 +2865,16 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
runTest("idea/testData/inspectionsLocal/liftOut/ifToReturn/innerIfTransformed.kt"); runTest("idea/testData/inspectionsLocal/liftOut/ifToReturn/innerIfTransformed.kt");
} }
@TestMetadata("onReturn.kt")
public void testOnReturn() throws Exception {
runTest("idea/testData/inspectionsLocal/liftOut/ifToReturn/onReturn.kt");
}
@TestMetadata("onReturn2.kt")
public void testOnReturn2() throws Exception {
runTest("idea/testData/inspectionsLocal/liftOut/ifToReturn/onReturn2.kt");
}
@TestMetadata("simpleIf.kt") @TestMetadata("simpleIf.kt")
public void testSimpleIf() throws Exception { public void testSimpleIf() throws Exception {
runTest("idea/testData/inspectionsLocal/liftOut/ifToReturn/simpleIf.kt"); runTest("idea/testData/inspectionsLocal/liftOut/ifToReturn/simpleIf.kt");
@@ -2996,6 +3006,11 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
runTest("idea/testData/inspectionsLocal/liftOut/tryToReturn/inner.kt"); runTest("idea/testData/inspectionsLocal/liftOut/tryToReturn/inner.kt");
} }
@TestMetadata("onReturn.kt")
public void testOnReturn() throws Exception {
runTest("idea/testData/inspectionsLocal/liftOut/tryToReturn/onReturn.kt");
}
@TestMetadata("withoutReturn.kt") @TestMetadata("withoutReturn.kt")
public void testWithoutReturn() throws Exception { public void testWithoutReturn() throws Exception {
runTest("idea/testData/inspectionsLocal/liftOut/tryToReturn/withoutReturn.kt"); runTest("idea/testData/inspectionsLocal/liftOut/tryToReturn/withoutReturn.kt");
@@ -3112,6 +3127,16 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
runTest("idea/testData/inspectionsLocal/liftOut/whenToReturn/localReturns.kt"); runTest("idea/testData/inspectionsLocal/liftOut/whenToReturn/localReturns.kt");
} }
@TestMetadata("onReturn.kt")
public void testOnReturn() throws Exception {
runTest("idea/testData/inspectionsLocal/liftOut/whenToReturn/onReturn.kt");
}
@TestMetadata("onReturn2.kt")
public void testOnReturn2() throws Exception {
runTest("idea/testData/inspectionsLocal/liftOut/whenToReturn/onReturn2.kt");
}
@TestMetadata("otherReturns.kt") @TestMetadata("otherReturns.kt")
public void testOtherReturns() throws Exception { public void testOtherReturns() throws Exception {
runTest("idea/testData/inspectionsLocal/liftOut/whenToReturn/otherReturns.kt"); runTest("idea/testData/inspectionsLocal/liftOut/whenToReturn/otherReturns.kt");