From b988fb701dbc0d6219a89dfa7ee0c5a27823dd16 Mon Sep 17 00:00:00 2001 From: Dimach Date: Mon, 14 Aug 2017 21:19:33 +0300 Subject: [PATCH] KT-17888 Inspection to warn about suspicious combination of == and === --- .../SuspiciousEqualsCombination.html | 5 ++ idea/src/META-INF/plugin.xml | 9 +++ .../SuspiciousEqualsCombination.kt | 69 +++++++++++++++++++ .../inspectionData/expected.xml | 37 ++++++++++ .../inspectionData/inspections.test | 1 + .../suspiciousEqualsCombination/test.kt | 15 ++++ .../codeInsight/InspectionTestGenerated.java | 6 ++ 7 files changed, 142 insertions(+) create mode 100644 idea/resources/inspectionDescriptions/SuspiciousEqualsCombination.html create mode 100644 idea/src/org/jetbrains/kotlin/idea/inspections/SuspiciousEqualsCombination.kt create mode 100644 idea/testData/inspections/suspiciousEqualsCombination/inspectionData/expected.xml create mode 100644 idea/testData/inspections/suspiciousEqualsCombination/inspectionData/inspections.test create mode 100644 idea/testData/inspections/suspiciousEqualsCombination/test.kt diff --git a/idea/resources/inspectionDescriptions/SuspiciousEqualsCombination.html b/idea/resources/inspectionDescriptions/SuspiciousEqualsCombination.html new file mode 100644 index 00000000000..5ed82080b86 --- /dev/null +++ b/idea/resources/inspectionDescriptions/SuspiciousEqualsCombination.html @@ -0,0 +1,5 @@ + + +This inspection reports when == and === used on same variable in one expression + + \ No newline at end of file diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 5b03a7c02fc..26f6cfd09d9 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -2546,6 +2546,15 @@ language="kotlin" /> + + diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/SuspiciousEqualsCombination.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/SuspiciousEqualsCombination.kt new file mode 100644 index 00000000000..af38c0a9b3b --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/SuspiciousEqualsCombination.kt @@ -0,0 +1,69 @@ +/* + * 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.CleanupLocalInspectionTool +import com.intellij.codeInspection.LocalInspectionToolSession +import com.intellij.codeInspection.ProblemHighlightType +import com.intellij.codeInspection.ProblemsHolder +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.* + +class SuspiciousEqualsCombination : AbstractKotlinInspection(), CleanupLocalInspectionTool { + override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession) = + object : KtVisitorVoid() { + override fun visitBinaryExpression(expression: KtBinaryExpression) { + if (expression.parent is KtBinaryExpression) return + val operands = expression.parseBinary() + val eqeq = operands.eqEqOperands.map { it.text } + val eqeqeq = operands.eqEqEqOperands.map { it.text } + if (eqeq.intersect(eqeqeq).isNotEmpty()) { + holder.registerProblem(expression, "Suspicious combination of == and ===", + ProblemHighlightType.GENERIC_ERROR_OR_WARNING) + } + } + } + + private fun KtBinaryExpression.parseBinary(pair: ComparisonOperands = ComparisonOperands()): ComparisonOperands { + when (operationToken) { + KtTokens.EQEQ, KtTokens.EXCLEQ -> { + (left as? KtNameReferenceExpression)?.let(pair.eqEqOperands::add) + (right as? KtNameReferenceExpression)?.let(pair.eqEqOperands::add) + } + KtTokens.EQEQEQ, KtTokens.EXCLEQEQEQ -> { + (left as? KtNameReferenceExpression)?.let(pair.eqEqEqOperands::add) + (right as? KtNameReferenceExpression)?.let(pair.eqEqEqOperands::add) + } + KtTokens.ANDAND, KtTokens.OROR -> { + right?.parseExpression(pair) + left?.parseExpression(pair) + } + } + return pair + } + + private fun KtExpression.parseExpression(pair: ComparisonOperands) { + when (this) { + is KtBinaryExpression -> parseBinary(pair) + is KtParenthesizedExpression -> expression?.parseExpression(pair) + is KtPrefixExpression -> if (operationToken == KtTokens.EXCL) baseExpression?.parseExpression(pair) + } + } +} + +private data class ComparisonOperands(val eqEqOperands: MutableList = mutableListOf(), + val eqEqEqOperands: MutableList = mutableListOf()) diff --git a/idea/testData/inspections/suspiciousEqualsCombination/inspectionData/expected.xml b/idea/testData/inspections/suspiciousEqualsCombination/inspectionData/expected.xml new file mode 100644 index 00000000000..39362e13dcf --- /dev/null +++ b/idea/testData/inspections/suspiciousEqualsCombination/inspectionData/expected.xml @@ -0,0 +1,37 @@ + + + test.kt + 5 + light_idea_test_case + + Suspicious combination of == and === + Suspicious combination of == and === + + + + test.kt + 6 + light_idea_test_case + + Suspicious combination of == and === + Suspicious combination of == and === + + + + test.kt + 7 + light_idea_test_case + + Suspicious combination of == and === + Suspicious combination of == and === + + + + test.kt + 9 + light_idea_test_case + + Suspicious combination of == and === + Suspicious combination of == and === + + \ No newline at end of file diff --git a/idea/testData/inspections/suspiciousEqualsCombination/inspectionData/inspections.test b/idea/testData/inspections/suspiciousEqualsCombination/inspectionData/inspections.test new file mode 100644 index 00000000000..91b27896db4 --- /dev/null +++ b/idea/testData/inspections/suspiciousEqualsCombination/inspectionData/inspections.test @@ -0,0 +1 @@ +// INSPECTION_CLASS: org.jetbrains.kotlin.idea.inspections.SuspiciousEqualsCombination \ No newline at end of file diff --git a/idea/testData/inspections/suspiciousEqualsCombination/test.kt b/idea/testData/inspections/suspiciousEqualsCombination/test.kt new file mode 100644 index 00000000000..a9559043972 --- /dev/null +++ b/idea/testData/inspections/suspiciousEqualsCombination/test.kt @@ -0,0 +1,15 @@ +object Main { + fun test() { + val type1 = Main + val type = Main + if (type === CONST1 || type == CONST2 && type === CONST3) return + if (type === CONST1 || type == CONST2 && (type === CONST3)) return + if (type === CONST1 || type == CONST2 && !(type === CONST3)) return + if (type === CONST1 || type1 == CONST2 && type === CONST3) return + if (type === CONST1 || type1 == CONST1 && type === CONST3) return + } + + val CONST1 = Main; + val CONST2 = Main; + val CONST3 = Main; +} \ 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 a90c8b47f03..b8b338efd5a 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java @@ -377,6 +377,12 @@ public class InspectionTestGenerated extends AbstractInspectionTest { doTest(fileName); } + @TestMetadata("suspiciousEqualsCombination/inspectionData/inspections.test") + public void testSuspiciousEqualsCombination_inspectionData_Inspections_test() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspections/suspiciousEqualsCombination/inspectionData/inspections.test"); + doTest(fileName); + } + @TestMetadata("twoSetOfTypeparameters/inspectionData/inspections.test") public void testTwoSetOfTypeparameters_inspectionData_Inspections_test() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspections/twoSetOfTypeparameters/inspectionData/inspections.test");