diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtPsiUtil.java b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtPsiUtil.java index aa5f0577011..1c03c85c407 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtPsiUtil.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtPsiUtil.java @@ -333,8 +333,15 @@ public class KtPsiUtil { } public static boolean isTrueConstant(@Nullable KtExpression condition) { - return (condition != null && condition.getNode().getElementType() == KtNodeTypes.BOOLEAN_CONSTANT && - condition.getNode().findChildByType(KtTokens.TRUE_KEYWORD) != null); + return isBooleanConstant(condition) && condition.getNode().findChildByType(KtTokens.TRUE_KEYWORD) != null; + } + + public static boolean isFalseConstant(@Nullable KtExpression condition) { + return isBooleanConstant(condition) && condition.getNode().findChildByType(KtTokens.FALSE_KEYWORD) != null; + } + + private static boolean isBooleanConstant(@Nullable KtExpression condition) { + return condition != null && condition.getNode().getElementType() == KtNodeTypes.BOOLEAN_CONSTANT; } public static boolean isAbstract(@NotNull KtDeclarationWithBody declaration) { diff --git a/idea/resources/inspectionDescriptions/RedundantlIf.html b/idea/resources/inspectionDescriptions/RedundantlIf.html new file mode 100644 index 00000000000..33098205189 --- /dev/null +++ b/idea/resources/inspectionDescriptions/RedundantlIf.html @@ -0,0 +1,21 @@ + + +Reports if statements which can be simplified to single return statements. +

+ For example: +

+    if (foo()) {
+       return true
+    } else {
+       return false
+    }
+
+ can be simplified to +
+    return foo()
+
+ +

+ + + diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 0fa0d686741..40b94719c76 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -1532,6 +1532,15 @@ language="kotlin" /> + + diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantIfInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantIfInspection.kt new file mode 100644 index 00000000000..82c8de26555 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantIfInspection.kt @@ -0,0 +1,77 @@ +/* + * Copyright 2010-2016 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.* +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiElementVisitor +import org.jetbrains.kotlin.psi.* + +class RedundantIfInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool { + + override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor { + return object : KtVisitorVoid() { + override fun visitIfExpression(expression: KtIfExpression) { + super.visitIfExpression(expression) + + if (expression.condition != null && isRedundant(expression)) { + holder.registerProblem(expression, + "Redundant 'if' statement", + ProblemHighlightType.LIKE_UNUSED_SYMBOL, + RemoveRedundantIf) + } + } + } + } + + private fun isRedundant(expression: KtIfExpression): Boolean { + val thenReturn = getReturnedExpression(expression.then) ?: return false + val elseReturn = getReturnedExpression(expression.`else`) ?: return false + + if (KtPsiUtil.isTrueConstant(thenReturn) && KtPsiUtil.isFalseConstant(elseReturn)) { + return true + } + + if (KtPsiUtil.isFalseConstant(thenReturn) && KtPsiUtil.isTrueConstant(elseReturn)) { + return true + } + + return false + } + + private fun getReturnedExpression(expression: KtExpression?) : KtExpression? { + when(expression) { + is KtReturnExpression -> return expression.returnedExpression + is KtBlockExpression -> { + val statement = expression.statements.singleOrNull() as? KtReturnExpression ?: return null + return statement.returnedExpression + } + else -> return null + } + } + + private object RemoveRedundantIf : LocalQuickFix { + override fun getName() = "Remove redundant 'if' statement" + override fun getFamilyName() = name + + override fun applyFix(project: Project, descriptor: ProblemDescriptor) { + val element = descriptor.psiElement as KtIfExpression + element.replace(KtPsiFactory(element).createExpressionByPattern("return $0", element.condition!!.text)); + } + } + +} diff --git a/idea/testData/inspections/redundantIf/inspectionData/expected.xml b/idea/testData/inspections/redundantIf/inspectionData/expected.xml new file mode 100644 index 00000000000..c9e8840c5a9 --- /dev/null +++ b/idea/testData/inspections/redundantIf/inspectionData/expected.xml @@ -0,0 +1,18 @@ + + + redundantIf.kt + 2 + light_idea_test_case + + Redundant 'if' statement + Redundant 'if' statement + + + redundantIf.kt + 10 + light_idea_test_case + + Redundant 'if' statement + Redundant 'if' statement + + diff --git a/idea/testData/inspections/redundantIf/inspectionData/inspections.test b/idea/testData/inspections/redundantIf/inspectionData/inspections.test new file mode 100644 index 00000000000..a8d3dc04796 --- /dev/null +++ b/idea/testData/inspections/redundantIf/inspectionData/inspections.test @@ -0,0 +1 @@ +// INSPECTION_CLASS: org.jetbrains.kotlin.idea.inspections.RedundantIfInspection diff --git a/idea/testData/inspections/redundantIf/redundantIf.kt b/idea/testData/inspections/redundantIf/redundantIf.kt new file mode 100644 index 00000000000..08f7e19e7a1 --- /dev/null +++ b/idea/testData/inspections/redundantIf/redundantIf.kt @@ -0,0 +1,11 @@ +fun foo() { + if (value % 2 == 0) { + return true + } else { + return false + } +} + +fun bar() { + if (value % 2 == 0) return true else return false +} diff --git a/idea/testData/quickfix/redundantIf/.inspection b/idea/testData/quickfix/redundantIf/.inspection new file mode 100644 index 00000000000..31f12fbfa2f --- /dev/null +++ b/idea/testData/quickfix/redundantIf/.inspection @@ -0,0 +1 @@ +org.jetbrains.kotlin.idea.inspections.RedundantIfInspection \ No newline at end of file diff --git a/idea/testData/quickfix/redundantIf/simple.kt b/idea/testData/quickfix/redundantIf/simple.kt new file mode 100644 index 00000000000..6f51f02b593 --- /dev/null +++ b/idea/testData/quickfix/redundantIf/simple.kt @@ -0,0 +1,8 @@ +// "Remove redundant 'if' statement" "true" +fun bar() { + if (value % 2 == 0) { + return true + } else { + return false + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/redundantIf/simplet.kt.after b/idea/testData/quickfix/redundantIf/simplet.kt.after new file mode 100644 index 00000000000..085569e3557 --- /dev/null +++ b/idea/testData/quickfix/redundantIf/simplet.kt.after @@ -0,0 +1,4 @@ +// "Remove redundant 'if' statement" "true" +fun bar() { + return value % 2 == 0 +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java index 1e808d85295..90c74ccc155 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java @@ -160,6 +160,12 @@ public class InspectionTestGenerated extends AbstractInspectionTest { doTest(fileName); } + @TestMetadata("redundantIf/inspectionData/inspections.test") + public void testRedundantIf_inspectionData_Inspections_test() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspections/redundantIf/inspectionData/inspections.test"); + doTest(fileName); + } + @TestMetadata("redundantModalityModifier/inspectionData/inspections.test") public void testRedundantModalityModifier_inspectionData_Inspections_test() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspections/redundantModalityModifier/inspectionData/inspections.test"); diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java index f844006867c..102f3988ef9 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -6383,6 +6383,21 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { } } + @TestMetadata("idea/testData/quickfix/redundantIf") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class RedundantIf extends AbstractQuickFixTest { + public void testAllFilesPresentInRedundantIf() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/redundantIf"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/redundantIf/simple.kt"); + doTest(fileName); + } + } + @TestMetadata("idea/testData/quickfix/redundantModalityModifier") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)