Inspection to warn on using == with array types #KT-13384 Fixed

This commit is contained in:
shiraji
2016-08-18 11:30:21 +03:00
committed by Mikhail Glukhikh
parent 7d35316d95
commit 1bbde54e9d
15 changed files with 176 additions and 0 deletions
@@ -0,0 +1,5 @@
<html>
<body>
This inspection detects '==' operator for arrays that should be replaced with 'Arrays.equals'
</body>
</html>
@@ -0,0 +1,6 @@
fun foo() {
val a = arrayOf("a", "b", "c")
val b = arrayOf("a", "b", "c")
if (<spot>Arrays.equals(a, b)</spot>) {
}
}
@@ -0,0 +1,6 @@
fun foo() {
val a = arrayOf("a", "b", "c")
val b = arrayOf("a", "b", "c")
if (<spot>a == b</spot>) {
}
}
@@ -0,0 +1,5 @@
<html>
<body>
This intention replace '==' operator for arrays with 'Arrays.equals'
</body>
</html>
+13
View File
@@ -1329,6 +1329,11 @@
<category>Kotlin</category>
</intentionAction>
<intentionAction>
<className>org.jetbrains.kotlin.idea.intentions.ReplaceArrayEqualityOpWithArraysEqualsIntention</className>
<category>Kotlin</category>
</intentionAction>
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.ObjectLiteralToLambdaInspection"
displayName="Object literal can be converted to lambda"
groupName="Kotlin"
@@ -1746,6 +1751,14 @@
language="kotlin"
/>
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.ReplaceArrayEqualityOpWithArraysEqualsInspection"
displayName="Replace '==' with 'Arrays.equals'"
groupName="Kotlin"
enabledByDefault="true"
level="WARNING"
language="kotlin"
/>
<referenceImporter implementation="org.jetbrains.kotlin.idea.quickfix.KotlinReferenceImporter"/>
<fileType.fileViewProviderFactory filetype="KJSM" implementationClass="com.intellij.psi.ClassFileViewProviderFactory"/>
@@ -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<KtBinaryExpression>(ReplaceArrayEqualityOpWithArraysEqualsIntention::class)
class ReplaceArrayEqualityOpWithArraysEqualsIntention : SelfTargetingOffsetIndependentIntention<KtBinaryExpression>(
KtBinaryExpression::class.java,
"Replace '==' with 'Arrays.equals'"
) {
private val arrayName = Name.identifier("Array")
private fun ResolvedCall<out CallableDescriptor>.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()
}
}
@@ -0,0 +1,10 @@
<problems>
<problem>
<file>test.kt</file>
<line>5</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/test.kt"/>
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">Replace '==' with 'Arrays.equals'</problem_class>
<description>Replace '==' with 'Arrays.equals'</description>
</problem>
</problems>
@@ -0,0 +1 @@
// INSPECTION_CLASS: org.jetbrains.kotlin.idea.intentions.ReplaceArrayEqualityOpWithArraysEqualsInspection
@@ -0,0 +1,7 @@
fun foo() {
val a = arrayOf("a")
val b = a
val c: Any? = null
a == b // YES
a == c // NO
}
@@ -0,0 +1 @@
org.jetbrains.kotlin.idea.intentions.ReplaceArrayEqualityOpWithArraysEqualsIntention
@@ -0,0 +1,8 @@
// IS_APPLICABLE: false
fun foo() {
val a = arrayOf("a", "b", "c")
val b: Any? = null
if (a <caret>== b) {
}
}
@@ -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 <caret>== b) {
}
}
@@ -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)) {
}
}
@@ -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");
@@ -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)