Refactoring: make "replace array equality ..." an inspection

This commit is contained in:
Mikhail Glukhikh
2017-12-18 18:23:29 +03:00
parent 33d94cd5e1
commit 756cb32eaf
19 changed files with 123 additions and 128 deletions
@@ -1,6 +0,0 @@
fun foo() {
val a = arrayOf("a", "b", "c")
val b = arrayOf("a", "b", "c")
if (<spot>Arrays.equals(a, b)</spot>) {
}
}
@@ -1,6 +0,0 @@
fun foo() {
val a = arrayOf("a", "b", "c")
val b = arrayOf("a", "b", "c")
if (<spot>a == b</spot>) {
}
}
@@ -1,5 +0,0 @@
<html>
<body>
This intention replace '==' or '!=' operator for arrays with 'Arrays.equals'
</body>
</html>
+1 -6
View File
@@ -1357,11 +1357,6 @@
<category>Kotlin</category>
</intentionAction>
<intentionAction>
<className>org.jetbrains.kotlin.idea.intentions.ReplaceArrayEqualityOpWithArraysEqualsIntention</className>
<category>Kotlin</category>
</intentionAction>
<intentionAction>
<className>org.jetbrains.kotlin.idea.intentions.ConvertCamelCaseTestFunctionToSpacedIntention</className>
<category>Kotlin</category>
@@ -2058,7 +2053,7 @@
language="kotlin"
/>
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.ReplaceArrayEqualityOpWithArraysEqualsInspection"
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.ReplaceArrayEqualityOpWithArraysEqualsInspection"
displayName="Replace '==' with 'Arrays.equals'"
groupPath="Kotlin"
groupName="Probable bugs"
@@ -0,0 +1,78 @@
/*
* 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.*
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.intentions.resolvedToArrayType
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtBinaryExpression
import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.KtVisitorVoid
import org.jetbrains.kotlin.psi.createExpressionByPattern
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
class ReplaceArrayEqualityOpWithArraysEqualsInspection : AbstractApplicabilityBasedInspection<KtBinaryExpression>() {
override fun applyTo(element: PsiElement, project: Project, editor: Editor?) {
val expression = element as? KtBinaryExpression ?: return
val right = expression.right ?: return
val left = expression.left ?: return
val factory = KtPsiFactory(project)
val template = buildString {
if (expression.operationToken == KtTokens.EXCLEQ) append("!")
append("$0.contentEquals($1)")
}
expression.replace(factory.createExpressionByPattern(template, left, right))
}
override fun isApplicable(element: KtBinaryExpression): Boolean {
val operationToken = element.operationToken
when (operationToken) {
KtTokens.EQEQ, KtTokens.EXCLEQ -> {}
else -> return false
}
val right = element.right
val left = element.left
if (right == null || left == null) return false
val context = element.analyze()
val rightResolvedCall = right.getResolvedCall(context)
val leftResolvedCall = left.getResolvedCall(context)
return rightResolvedCall?.resolvedToArrayType() == true && leftResolvedCall?.resolvedToArrayType() == true
}
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession) =
object : KtVisitorVoid() {
override fun visitBinaryExpression(expression: KtBinaryExpression) {
super.visitBinaryExpression(expression)
visitTargetElement(expression, holder, isOnTheFly)
}
}
override fun inspectionText(element: KtBinaryExpression) = "Dangerous array comparison"
override val defaultFixText: String
get() = "Replace with 'contentEquals'"
override fun fixText(element: KtBinaryExpression): String = when (element.operationToken) {
KtTokens.EQEQ -> "Replace '==' with 'contentEquals'"
KtTokens.EXCLEQ -> "Replace '!=' with 'contentEquals'"
else -> ""
}
}
@@ -1,61 +0,0 @@
/*
* 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.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.inspections.IntentionBasedInspection
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtBinaryExpression
import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.createExpressionByPattern
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
class ReplaceArrayEqualityOpWithArraysEqualsInspection :
IntentionBasedInspection<KtBinaryExpression>(ReplaceArrayEqualityOpWithArraysEqualsIntention::class)
class ReplaceArrayEqualityOpWithArraysEqualsIntention : SelfTargetingOffsetIndependentIntention<KtBinaryExpression>(
KtBinaryExpression::class.java,
"Replace '==' with 'contentEquals'"
) {
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 template = buildString {
if (element.operationToken == KtTokens.EXCLEQ) append("!")
append("$0.contentEquals($1)")
}
val expression = factory.createExpressionByPattern(template, left, right)
element.replace(expression)
}
override fun isApplicableTo(element: KtBinaryExpression): Boolean {
val operationToken = element.operationToken
if (operationToken != KtTokens.EQEQ && operationToken != KtTokens.EXCLEQ) return false
if (operationToken == KtTokens.EXCLEQ) text = "Replace '!=' with 'contentEquals'"
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()
}
}
@@ -5,7 +5,7 @@
<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 'contentEquals'</description>
<description>Dangerous array comparison</description>
</problem>
<problem>
<file>test.kt</file>
@@ -13,6 +13,6 @@
<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 'contentEquals'</description>
<description>Dangerous array comparison</description>
</problem>
</problems>
@@ -1 +1 @@
// INSPECTION_CLASS: org.jetbrains.kotlin.idea.intentions.ReplaceArrayEqualityOpWithArraysEqualsInspection
// INSPECTION_CLASS: org.jetbrains.kotlin.idea.inspections.ReplaceArrayEqualityOpWithArraysEqualsInspection
@@ -0,0 +1 @@
org.jetbrains.kotlin.idea.inspections.ReplaceArrayEqualityOpWithArraysEqualsInspection
@@ -1,4 +1,4 @@
// IS_APPLICABLE: false
// PROBLEM: none
fun foo() {
val a = arrayOf("a", "b", "c")
@@ -1,5 +1,5 @@
// WITH_RUNTIME
// INTENTION_TEXT: Replace '==' with 'contentEquals'
// FIX: Replace '==' with 'contentEquals'
fun foo() {
val a = arrayOf("a", "b", "c")
val b = arrayOf("a", "b", "c")
@@ -1,5 +1,5 @@
// WITH_RUNTIME
// INTENTION_TEXT: Replace '==' with 'contentEquals'
// FIX: Replace '==' with 'contentEquals'
fun foo() {
val a = arrayOf("a", "b", "c")
val b = arrayOf("a", "b", "c")
@@ -1,5 +1,5 @@
// WITH_RUNTIME
// INTENTION_TEXT: Replace '!=' with 'contentEquals'
// FIX: Replace '!=' with 'contentEquals'
fun foo() {
val a = arrayOf("a", "b", "c")
val b = arrayOf("a", "b", "c")
@@ -1,5 +1,5 @@
// WITH_RUNTIME
// INTENTION_TEXT: Replace '!=' with 'contentEquals'
// FIX: Replace '!=' with 'contentEquals'
fun foo() {
val a = arrayOf("a", "b", "c")
val b = arrayOf("a", "b", "c")
@@ -1,5 +1,5 @@
// WITH_RUNTIME
// INTENTION_TEXT: Replace '==' with 'contentEquals'
// FIX: Replace '==' with 'contentEquals'
fun foo() {
val a = charArrayOf('a', 'b', 'c')
val b = charArrayOf('a', 'b', 'c')
@@ -1,5 +1,5 @@
// WITH_RUNTIME
// INTENTION_TEXT: Replace '==' with 'contentEquals'
// FIX: Replace '==' with 'contentEquals'
fun foo() {
val a = charArrayOf('a', 'b', 'c')
val b = charArrayOf('a', 'b', 'c')
@@ -1 +0,0 @@
org.jetbrains.kotlin.idea.intentions.ReplaceArrayEqualityOpWithArraysEqualsIntention
@@ -2379,6 +2379,39 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
}
}
@TestMetadata("idea/testData/inspectionsLocal/replaceArrayEqualityOpWithArraysEquals")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class ReplaceArrayEqualityOpWithArraysEquals extends AbstractLocalInspectionTest {
public void testAllFilesPresentInReplaceArrayEqualityOpWithArraysEquals() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/inspectionsLocal/replaceArrayEqualityOpWithArraysEquals"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true);
}
@TestMetadata("arrayAndOtherTypeEQEQ.kt")
public void testArrayAndOtherTypeEQEQ() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/replaceArrayEqualityOpWithArraysEquals/arrayAndOtherTypeEQEQ.kt");
doTest(fileName);
}
@TestMetadata("arrayEQEQ.kt")
public void testArrayEQEQ() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/replaceArrayEqualityOpWithArraysEquals/arrayEQEQ.kt");
doTest(fileName);
}
@TestMetadata("arrayEXCLEQ.kt")
public void testArrayEXCLEQ() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/replaceArrayEqualityOpWithArraysEquals/arrayEXCLEQ.kt");
doTest(fileName);
}
@TestMetadata("primitiveArrayEQEQ.kt")
public void testPrimitiveArrayEQEQ() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/replaceArrayEqualityOpWithArraysEquals/primitiveArrayEQEQ.kt");
doTest(fileName);
}
}
@TestMetadata("idea/testData/inspectionsLocal/replaceArrayOfWithLiteral")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -13899,39 +13899,6 @@ 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|kts)$"), TargetBackend.ANY, 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("arrayEXCLEQ.kt")
public void testArrayEXCLEQ() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/replaceArrayEqualityOpWithArraysEquals/arrayEXCLEQ.kt");
doTest(fileName);
}
@TestMetadata("primitiveArrayEQEQ.kt")
public void testPrimitiveArrayEQEQ() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/replaceArrayEqualityOpWithArraysEquals/primitiveArrayEQEQ.kt");
doTest(fileName);
}
}
@TestMetadata("idea/testData/intentions/replaceExplicitFunctionLiteralParamWithIt")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)