diff --git a/idea/resources/inspectionDescriptions/ReplaceArrayEqualityOpWithArraysEquals.html b/idea/resources/inspectionDescriptions/ReplaceArrayEqualityOpWithArraysEquals.html new file mode 100644 index 00000000000..74bf53dda29 --- /dev/null +++ b/idea/resources/inspectionDescriptions/ReplaceArrayEqualityOpWithArraysEquals.html @@ -0,0 +1,5 @@ + + +This inspection detects '==' operator for arrays that should be replaced with 'Arrays.equals' + + \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/ReplaceArrayEqualityOpWithArraysEqualsIntention/after.kt.template b/idea/resources/intentionDescriptions/ReplaceArrayEqualityOpWithArraysEqualsIntention/after.kt.template new file mode 100644 index 00000000000..5c6b1e7d565 --- /dev/null +++ b/idea/resources/intentionDescriptions/ReplaceArrayEqualityOpWithArraysEqualsIntention/after.kt.template @@ -0,0 +1,6 @@ +fun foo() { + val a = arrayOf("a", "b", "c") + val b = arrayOf("a", "b", "c") + if (Arrays.equals(a, b)) { + } +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/ReplaceArrayEqualityOpWithArraysEqualsIntention/before.kt.template b/idea/resources/intentionDescriptions/ReplaceArrayEqualityOpWithArraysEqualsIntention/before.kt.template new file mode 100644 index 00000000000..827c44c386d --- /dev/null +++ b/idea/resources/intentionDescriptions/ReplaceArrayEqualityOpWithArraysEqualsIntention/before.kt.template @@ -0,0 +1,6 @@ +fun foo() { + val a = arrayOf("a", "b", "c") + val b = arrayOf("a", "b", "c") + if (a == b) { + } +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/ReplaceArrayEqualityOpWithArraysEqualsIntention/description.html b/idea/resources/intentionDescriptions/ReplaceArrayEqualityOpWithArraysEqualsIntention/description.html new file mode 100644 index 00000000000..4d248a19655 --- /dev/null +++ b/idea/resources/intentionDescriptions/ReplaceArrayEqualityOpWithArraysEqualsIntention/description.html @@ -0,0 +1,5 @@ + + +This intention replace '==' operator for arrays with 'Arrays.equals' + + \ No newline at end of file diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index d66a357dd95..a59d2bfc8fa 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -1329,6 +1329,11 @@ Kotlin + + org.jetbrains.kotlin.idea.intentions.ReplaceArrayEqualityOpWithArraysEqualsIntention + Kotlin + + + + diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/ReplaceArrayEqualityOpWithArraysEqualsIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/ReplaceArrayEqualityOpWithArraysEqualsIntention.kt new file mode 100644 index 00000000000..4e1754f617a --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/ReplaceArrayEqualityOpWithArraysEqualsIntention.kt @@ -0,0 +1,69 @@ +/* + * 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.intentions + +import com.intellij.openapi.editor.Editor +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference +import org.jetbrains.kotlin.idea.inspections.IntentionBasedInspection +import org.jetbrains.kotlin.idea.util.ImportInsertHelper +import org.jetbrains.kotlin.js.descriptorUtils.nameIfStandardType +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.KtBinaryExpression +import org.jetbrains.kotlin.psi.KtPsiFactory +import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall + +class ReplaceArrayEqualityOpWithArraysEqualsInspection : + IntentionBasedInspection(ReplaceArrayEqualityOpWithArraysEqualsIntention::class) + +class ReplaceArrayEqualityOpWithArraysEqualsIntention : SelfTargetingOffsetIndependentIntention( + KtBinaryExpression::class.java, + "Replace '==' with 'Arrays.equals'" +) { + private val arrayName = Name.identifier("Array") + + private fun ResolvedCall.resolvedToArrayType() = + resultingDescriptor.returnType?.nameIfStandardType == arrayName + + override fun applyTo(element: KtBinaryExpression, editor: Editor?) { + val project = element.project + val right = element.right ?: return + val left = element.left ?: return + val factory = KtPsiFactory(project) + val ktFile = element.getContainingKtFile() + ktFile.resolveImportReference(FqName("java.util.Arrays")).firstOrNull()?.let { + ImportInsertHelper.getInstance(project).importDescriptor(ktFile, it) + } + val expression = factory.createExpression("Arrays.equals(${left.text}, ${right.text})") + element.replace(expression) + } + + override fun isApplicableTo(element: KtBinaryExpression): Boolean { + if (element.operationToken != KtTokens.EQEQ) return false + val right = element.right ?: return false + val left = element.left ?: return false + val rightResolvedCall = right.getResolvedCall(right.analyze()) ?: return false + + if (!rightResolvedCall.resolvedToArrayType()) return false + val leftResolvedCall = left.getResolvedCall(left.analyze()) ?: return false + return leftResolvedCall.resolvedToArrayType() + } +} \ No newline at end of file diff --git a/idea/testData/inspections/replaceArrayEqualityOpWithArraysEquals/inspectionData/expected.xml b/idea/testData/inspections/replaceArrayEqualityOpWithArraysEquals/inspectionData/expected.xml new file mode 100644 index 00000000000..0bf7675fc66 --- /dev/null +++ b/idea/testData/inspections/replaceArrayEqualityOpWithArraysEquals/inspectionData/expected.xml @@ -0,0 +1,10 @@ + + + test.kt + 5 + light_idea_test_case + + Replace '==' with 'Arrays.equals' + Replace '==' with 'Arrays.equals' + + \ No newline at end of file diff --git a/idea/testData/inspections/replaceArrayEqualityOpWithArraysEquals/inspectionData/inspections.test b/idea/testData/inspections/replaceArrayEqualityOpWithArraysEquals/inspectionData/inspections.test new file mode 100644 index 00000000000..85307db6048 --- /dev/null +++ b/idea/testData/inspections/replaceArrayEqualityOpWithArraysEquals/inspectionData/inspections.test @@ -0,0 +1 @@ +// INSPECTION_CLASS: org.jetbrains.kotlin.idea.intentions.ReplaceArrayEqualityOpWithArraysEqualsInspection \ No newline at end of file diff --git a/idea/testData/inspections/replaceArrayEqualityOpWithArraysEquals/test.kt b/idea/testData/inspections/replaceArrayEqualityOpWithArraysEquals/test.kt new file mode 100644 index 00000000000..b0a0896a5da --- /dev/null +++ b/idea/testData/inspections/replaceArrayEqualityOpWithArraysEquals/test.kt @@ -0,0 +1,7 @@ +fun foo() { + val a = arrayOf("a") + val b = a + val c: Any? = null + a == b // YES + a == c // NO +} \ No newline at end of file diff --git a/idea/testData/intentions/replaceArrayEqualityOpWithArraysEquals/.intention b/idea/testData/intentions/replaceArrayEqualityOpWithArraysEquals/.intention new file mode 100644 index 00000000000..3fb35aed18c --- /dev/null +++ b/idea/testData/intentions/replaceArrayEqualityOpWithArraysEquals/.intention @@ -0,0 +1 @@ +org.jetbrains.kotlin.idea.intentions.ReplaceArrayEqualityOpWithArraysEqualsIntention diff --git a/idea/testData/intentions/replaceArrayEqualityOpWithArraysEquals/arrayAndOtherTypeEQEQ.kt b/idea/testData/intentions/replaceArrayEqualityOpWithArraysEquals/arrayAndOtherTypeEQEQ.kt new file mode 100644 index 00000000000..3031f48cd2c --- /dev/null +++ b/idea/testData/intentions/replaceArrayEqualityOpWithArraysEquals/arrayAndOtherTypeEQEQ.kt @@ -0,0 +1,8 @@ +// IS_APPLICABLE: false + +fun foo() { + val a = arrayOf("a", "b", "c") + val b: Any? = null + if (a == b) { + } +} diff --git a/idea/testData/intentions/replaceArrayEqualityOpWithArraysEquals/arrayEQEQ.kt b/idea/testData/intentions/replaceArrayEqualityOpWithArraysEquals/arrayEQEQ.kt new file mode 100644 index 00000000000..8bec0b11c05 --- /dev/null +++ b/idea/testData/intentions/replaceArrayEqualityOpWithArraysEquals/arrayEQEQ.kt @@ -0,0 +1,8 @@ +// WITH_RUNTIME +// INTENTION_TEXT: Replace '==' with 'Arrays.equals' +fun foo() { + val a = arrayOf("a", "b", "c") + val b = arrayOf("a", "b", "c") + if (a == b) { + } +} diff --git a/idea/testData/intentions/replaceArrayEqualityOpWithArraysEquals/arrayEQEQ.kt.after b/idea/testData/intentions/replaceArrayEqualityOpWithArraysEquals/arrayEQEQ.kt.after new file mode 100644 index 00000000000..106dec0a540 --- /dev/null +++ b/idea/testData/intentions/replaceArrayEqualityOpWithArraysEquals/arrayEQEQ.kt.after @@ -0,0 +1,10 @@ +import java.util.Arrays + +// WITH_RUNTIME +// INTENTION_TEXT: Replace '==' with 'Arrays.equals' +fun foo() { + val a = arrayOf("a", "b", "c") + val b = arrayOf("a", "b", "c") + if (Arrays.equals(a, b)) { + } +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java index 4a52b4f157a..a635b678d9a 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java @@ -238,6 +238,12 @@ public class InspectionTestGenerated extends AbstractInspectionTest { doTest(fileName); } + @TestMetadata("replaceArrayEqualityOpWithArraysEquals/inspectionData/inspections.test") + public void testReplaceArrayEqualityOpWithArraysEquals_inspectionData_Inspections_test() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspections/replaceArrayEqualityOpWithArraysEquals/inspectionData/inspections.test"); + doTest(fileName); + } + @TestMetadata("replaceCallWithComparison/inspectionData/inspections.test") public void testReplaceCallWithComparison_inspectionData_Inspections_test() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspections/replaceCallWithComparison/inspectionData/inspections.test"); diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java index f7c0f3578c1..5c86385ccb0 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -9965,6 +9965,27 @@ public class IntentionTestGenerated extends AbstractIntentionTest { } } + @TestMetadata("idea/testData/intentions/replaceArrayEqualityOpWithArraysEquals") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ReplaceArrayEqualityOpWithArraysEquals extends AbstractIntentionTest { + public void testAllFilesPresentInReplaceArrayEqualityOpWithArraysEquals() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/replaceArrayEqualityOpWithArraysEquals"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true); + } + + @TestMetadata("arrayAndOtherTypeEQEQ.kt") + public void testArrayAndOtherTypeEQEQ() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/replaceArrayEqualityOpWithArraysEquals/arrayAndOtherTypeEQEQ.kt"); + doTest(fileName); + } + + @TestMetadata("arrayEQEQ.kt") + public void testArrayEQEQ() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/replaceArrayEqualityOpWithArraysEquals/arrayEQEQ.kt"); + doTest(fileName); + } + } + @TestMetadata("idea/testData/intentions/replaceExplicitFunctionLiteralParamWithIt") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)