diff --git a/idea/resources/inspectionDescriptions/CascadeIf.html b/idea/resources/inspectionDescriptions/CascadeIf.html new file mode 100644 index 00000000000..49c125bcf97 --- /dev/null +++ b/idea/resources/inspectionDescriptions/CascadeIf.html @@ -0,0 +1,5 @@ + + +This inspection reports if statements with three or more branches that can be replaced with when. + + \ No newline at end of file diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index d563bac8406..3f3390b0da7 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -2369,6 +2369,15 @@ language="kotlin" /> + + diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/CascadeIfInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/CascadeIfInspection.kt new file mode 100644 index 00000000000..09e6f5b0ba8 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/CascadeIfInspection.kt @@ -0,0 +1,57 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.inspections + +import com.intellij.codeInspection.IntentionWrapper +import com.intellij.codeInspection.LocalInspectionToolSession +import com.intellij.codeInspection.ProblemHighlightType +import com.intellij.codeInspection.ProblemsHolder +import org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.IfToWhenIntention +import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isIfBranch +import org.jetbrains.kotlin.idea.intentions.branches +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType +import org.jetbrains.kotlin.psi.psiUtil.lastBlockStatementOrThis + +class CascadeIfInspection : AbstractKotlinInspection() { + override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession) = + object : KtVisitorVoid() { + override fun visitIfExpression(expression: KtIfExpression) { + super.visitIfExpression(expression) + + val branches = expression.branches + if (branches.size <= 2) return + if (branches.any { + it == null || + it.lastBlockStatementOrThis() is KtIfExpression + }) return + + if (expression.isIfBranch()) return + + if (expression.anyDescendantOfType { + it is KtBreakExpression || it is KtContinueExpression + }) return + + holder.registerProblem( + expression.ifKeyword, + "Cascade if can be replaced with when", + ProblemHighlightType.GENERIC_ERROR_OR_WARNING, + IntentionWrapper(IfToWhenIntention(), expression.containingKtFile) + ) + } + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/cascadeIf/.inspection b/idea/testData/inspectionsLocal/cascadeIf/.inspection new file mode 100644 index 00000000000..53f620aa35e --- /dev/null +++ b/idea/testData/inspectionsLocal/cascadeIf/.inspection @@ -0,0 +1 @@ +org.jetbrains.kotlin.idea.inspections.CascadeIfInspection \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/cascadeIf/four.kt b/idea/testData/inspectionsLocal/cascadeIf/four.kt new file mode 100644 index 00000000000..5e44f5595e0 --- /dev/null +++ b/idea/testData/inspectionsLocal/cascadeIf/four.kt @@ -0,0 +1,16 @@ +// PROBLEM: none + +fun foo(a: Boolean, b: Boolean, c: Boolean) { + if (a) { + + } + else if (b) { + + } + else if (c) { + + } + else { + + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/cascadeIf/ifTree.kt b/idea/testData/inspectionsLocal/cascadeIf/ifTree.kt new file mode 100644 index 00000000000..6c164121e3e --- /dev/null +++ b/idea/testData/inspectionsLocal/cascadeIf/ifTree.kt @@ -0,0 +1,16 @@ +// PROBLEM: none + +fun println(s: String) {} + +fun foo(a: Boolean, b: Boolean) { + if (a) { + if (b) println("ab") + else println("a") + } + else if (b) { + println("b") + } + else { + println("none") + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/cascadeIf/insideOtherIf.kt b/idea/testData/inspectionsLocal/cascadeIf/insideOtherIf.kt new file mode 100644 index 00000000000..68334ba4c26 --- /dev/null +++ b/idea/testData/inspectionsLocal/cascadeIf/insideOtherIf.kt @@ -0,0 +1,15 @@ +fun println(s: String) {} + +fun test(x: Double, a: Boolean, b: Boolean) { + if (x > 0.0) { + if (a) { + println("a") + } + else if (b) { + println("b") + } + else { + println("none") + } + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/cascadeIf/insideOtherIf.kt.after b/idea/testData/inspectionsLocal/cascadeIf/insideOtherIf.kt.after new file mode 100644 index 00000000000..3db0953e3e8 --- /dev/null +++ b/idea/testData/inspectionsLocal/cascadeIf/insideOtherIf.kt.after @@ -0,0 +1,11 @@ +fun println(s: String) {} + +fun test(x: Double, a: Boolean, b: Boolean) { + if (x > 0.0) { + when { + a -> println("a") + b -> println("b") + else -> println("none") + } + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/cascadeIf/middleIf.kt b/idea/testData/inspectionsLocal/cascadeIf/middleIf.kt new file mode 100644 index 00000000000..d419479d610 --- /dev/null +++ b/idea/testData/inspectionsLocal/cascadeIf/middleIf.kt @@ -0,0 +1,15 @@ +// PROBLEM: none + +fun println(s: String) {} + +fun test(a: Boolean, b: Boolean) { + if (a) { + println("a") + } + else if (b) { + println("b") + } + else { + println("none") + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/cascadeIf/noSecondElse.kt b/idea/testData/inspectionsLocal/cascadeIf/noSecondElse.kt new file mode 100644 index 00000000000..162ce5a76cb --- /dev/null +++ b/idea/testData/inspectionsLocal/cascadeIf/noSecondElse.kt @@ -0,0 +1,12 @@ +// PROBLEM: none + +fun println(s: String) {} + +fun test(a: Boolean, b: Boolean) { + if (a) { + println("a") + } + else if (b) { + println("b") + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/cascadeIf/normal.kt b/idea/testData/inspectionsLocal/cascadeIf/normal.kt new file mode 100644 index 00000000000..9c25978881b --- /dev/null +++ b/idea/testData/inspectionsLocal/cascadeIf/normal.kt @@ -0,0 +1,13 @@ +fun println(s: String) {} + +fun test(a: Boolean, b: Boolean) { + if (a) { + println("a") + } + else if (b) { + println("b") + } + else { + println("none") + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/cascadeIf/normal.kt.after b/idea/testData/inspectionsLocal/cascadeIf/normal.kt.after new file mode 100644 index 00000000000..0bf6de068aa --- /dev/null +++ b/idea/testData/inspectionsLocal/cascadeIf/normal.kt.after @@ -0,0 +1,9 @@ +fun println(s: String) {} + +fun test(a: Boolean, b: Boolean) { + when { + a -> println("a") + b -> println("b") + else -> println("none") + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/cascadeIf/shortIf.kt b/idea/testData/inspectionsLocal/cascadeIf/shortIf.kt new file mode 100644 index 00000000000..41cea1b8064 --- /dev/null +++ b/idea/testData/inspectionsLocal/cascadeIf/shortIf.kt @@ -0,0 +1,3 @@ +// PROBLEM: none + +val x = if (2 > 4) 1 else -1 \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/cascadeIf/withBreak.kt b/idea/testData/inspectionsLocal/cascadeIf/withBreak.kt new file mode 100644 index 00000000000..69f1dc0d364 --- /dev/null +++ b/idea/testData/inspectionsLocal/cascadeIf/withBreak.kt @@ -0,0 +1,17 @@ +// PROBLEM: none + +fun println(s: String) {} + +fun foo(size: Int, a: Boolean, b: Boolean) { + for (i in 1..size) { + if (a) { + break + } + else if (b) { + println("$i") + } + else { + println("*") + } + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java index df1a0ac30a5..6783597d59a 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java @@ -51,6 +51,63 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { } } + @TestMetadata("idea/testData/inspectionsLocal/cascadeIf") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class CascadeIf extends AbstractLocalInspectionTest { + public void testAllFilesPresentInCascadeIf() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/inspectionsLocal/cascadeIf"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("four.kt") + public void testFour() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/cascadeIf/four.kt"); + doTest(fileName); + } + + @TestMetadata("ifTree.kt") + public void testIfTree() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/cascadeIf/ifTree.kt"); + doTest(fileName); + } + + @TestMetadata("insideOtherIf.kt") + public void testInsideOtherIf() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/cascadeIf/insideOtherIf.kt"); + doTest(fileName); + } + + @TestMetadata("middleIf.kt") + public void testMiddleIf() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/cascadeIf/middleIf.kt"); + doTest(fileName); + } + + @TestMetadata("noSecondElse.kt") + public void testNoSecondElse() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/cascadeIf/noSecondElse.kt"); + doTest(fileName); + } + + @TestMetadata("normal.kt") + public void testNormal() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/cascadeIf/normal.kt"); + doTest(fileName); + } + + @TestMetadata("shortIf.kt") + public void testShortIf() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/cascadeIf/shortIf.kt"); + doTest(fileName); + } + + @TestMetadata("withBreak.kt") + public void testWithBreak() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/cascadeIf/withBreak.kt"); + doTest(fileName); + } + } + @TestMetadata("idea/testData/inspectionsLocal/collections") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)