Add inspection to move the variable declaration into when

#KT-29001 Fixed
This commit is contained in:
Dmitry Gridin
2019-02-14 13:25:27 +03:00
parent 7abe9333c8
commit 33954c7632
15 changed files with 220 additions and 0 deletions
@@ -2932,6 +2932,15 @@
language="kotlin"
/>
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.MoveVariableDeclarationIntoWhenInspection"
displayName="Variable declaration could be moved inside `when`"
groupPath="Kotlin"
groupName="Style issues"
enabledByDefault="true"
level="WEAK WARNING"
language="kotlin"
/>
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.MainFunctionReturnUnitInspection"
displayName="Entry point function should return Unit"
groupPath="Kotlin"
@@ -0,0 +1,5 @@
<html>
<body>
This inspection reports the variable declaration that can be moved inside <code>when</code> expression.
</body>
</html>
@@ -0,0 +1,55 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
import com.intellij.codeInspection.CleanupLocalInspectionTool
import com.intellij.codeInspection.LocalQuickFix
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElementVisitor
import com.intellij.psi.SmartPsiElementPointer
import org.jetbrains.kotlin.idea.intentions.loopToCallChain.countUsages
import org.jetbrains.kotlin.idea.intentions.loopToCallChain.previousStatement
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.createSmartPointer
class MoveVariableDeclarationIntoWhenInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor =
whenExpressionVisitor(fun(expression: KtWhenExpression) {
val subjectExpression = expression.subjectExpression ?: return
val property = expression.findDeclarationNear() ?: return
if (!property.isUsedOnlyIn(expression)) return
val identifier = property.nameIdentifier ?: return
holder.registerProblem(
property,
TextRange.from(identifier.startOffsetInParent, identifier.textLength),
"Variable declaration could be moved inside `when`",
MoveVariableDeclarationIntoWhenFix(subjectExpression.createSmartPointer())
)
})
}
private fun KtProperty.isUsedOnlyIn(element: KtElement): Boolean = countUsages() == countUsages(element)
private fun KtWhenExpression.findDeclarationNear(): KtProperty? {
val previousProperty = previousStatement() as? KtProperty ?: return null
return previousProperty.takeIf { !it.isVar && it.hasInitializer() && it.nameIdentifier?.text == subjectExpression?.text }
}
private class MoveVariableDeclarationIntoWhenFix(val subjectExpressionPointer: SmartPsiElementPointer<KtExpression>) : LocalQuickFix {
override fun getName() = "Move the variable into `when`"
override fun getFamilyName() = name
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val property = descriptor.psiElement as? KtProperty ?: return
val subjectExpression = subjectExpressionPointer.element ?: return
subjectExpression.replace(property.copy())
property.delete()
}
}
@@ -0,0 +1 @@
org.jetbrains.kotlin.idea.inspections.MoveVariableDeclarationIntoWhenInspection
@@ -0,0 +1,11 @@
// PROBLEM: none
fun foo() {
val a = 1
val b<caret> = 1
when (a) {
1 -> {
}
else -> {
}
}
}
@@ -0,0 +1,11 @@
// PROBLEM: none
fun foo() {
val a<caret> = 1
val b = 42
when (a) {
1 -> {
}
else -> {
}
}
}
@@ -0,0 +1,11 @@
// PROBLEM: none
fun foo() {
val a<caret> = 1
when (a) {
1 -> {
}
else -> {
}
}
val b = a
}
@@ -0,0 +1,10 @@
// PROBLEM: none
fun foo() {
var a<caret> = 1
when (a) {
1 -> {
}
else -> {
}
}
}
@@ -0,0 +1,9 @@
fun test() = 42
fun foo() {
val a<caret> = test()
when (a) {
1 -> a
else -> 24
}
}
@@ -0,0 +1,8 @@
fun test() = 42
fun foo() {
when (val a = test()) {
1 -> a
else -> 24
}
}
@@ -0,0 +1,11 @@
fun foo() {
val a<caret> = 1
// comment
when (a) {
1 -> {
}
else -> {
}
}
}
@@ -0,0 +1,10 @@
fun foo() {
// comment
when (val a = 1) {
1 -> {
}
else -> {
}
}
}
@@ -0,0 +1,11 @@
fun foo() {
val a<caret> = 1
when (a) {
1 -> {
}
else -> {
}
}
}
@@ -0,0 +1,10 @@
fun foo() {
when (val a = 1) {
1 -> {
}
else -> {
}
}
}
@@ -4277,6 +4277,54 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
}
}
@TestMetadata("idea/testData/inspectionsLocal/moveVariableDeclarationIntoWhen")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class MoveVariableDeclarationIntoWhen extends AbstractLocalInspectionTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
}
public void testAllFilesPresentInMoveVariableDeclarationIntoWhen() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/inspectionsLocal/moveVariableDeclarationIntoWhen"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true);
}
@TestMetadata("notApplicableOtherName.kt")
public void testNotApplicableOtherName() throws Exception {
runTest("idea/testData/inspectionsLocal/moveVariableDeclarationIntoWhen/notApplicableOtherName.kt");
}
@TestMetadata("notApplicableSideEffect.kt")
public void testNotApplicableSideEffect() throws Exception {
runTest("idea/testData/inspectionsLocal/moveVariableDeclarationIntoWhen/notApplicableSideEffect.kt");
}
@TestMetadata("notApplicableUsedInOtherScope.kt")
public void testNotApplicableUsedInOtherScope() throws Exception {
runTest("idea/testData/inspectionsLocal/moveVariableDeclarationIntoWhen/notApplicableUsedInOtherScope.kt");
}
@TestMetadata("notApplicableVar.kt")
public void testNotApplicableVar() throws Exception {
runTest("idea/testData/inspectionsLocal/moveVariableDeclarationIntoWhen/notApplicableVar.kt");
}
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
runTest("idea/testData/inspectionsLocal/moveVariableDeclarationIntoWhen/simple.kt");
}
@TestMetadata("withComment.kt")
public void testWithComment() throws Exception {
runTest("idea/testData/inspectionsLocal/moveVariableDeclarationIntoWhen/withComment.kt");
}
@TestMetadata("withNewLine.kt")
public void testWithNewLine() throws Exception {
runTest("idea/testData/inspectionsLocal/moveVariableDeclarationIntoWhen/withNewLine.kt");
}
}
@TestMetadata("idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)