Augmented list assignment inspection: add "replace with ordinary" fix

Relates to KT-20626
This commit is contained in:
Mikhail Glukhikh
2018-12-04 13:18:21 +03:00
parent ed8305995e
commit aa9e48b9b6
26 changed files with 80 additions and 35 deletions
@@ -5,10 +5,7 @@
package org.jetbrains.kotlin.idea.inspections package org.jetbrains.kotlin.idea.inspections
import com.intellij.codeInspection.LocalQuickFix import com.intellij.codeInspection.*
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElementVisitor import com.intellij.psi.PsiElementVisitor
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
@@ -17,15 +14,13 @@ import org.jetbrains.kotlin.diagnostics.Severity
import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
import org.jetbrains.kotlin.idea.core.replaced import org.jetbrains.kotlin.idea.core.replaced
import org.jetbrains.kotlin.idea.intentions.ReplaceWithOrdinaryAssignmentIntention
import org.jetbrains.kotlin.idea.project.builtIns import org.jetbrains.kotlin.idea.project.builtIns
import org.jetbrains.kotlin.idea.references.mainReference import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.lexer.KtSingleValueToken import org.jetbrains.kotlin.lexer.KtSingleValueToken
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.getPrevSiblingIgnoringWhitespaceAndComments
import org.jetbrains.kotlin.psi.psiUtil.siblings
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.callUtil.getType import org.jetbrains.kotlin.resolve.calls.callUtil.getType
@@ -60,11 +55,10 @@ class SuspiciousCollectionReassignmentInspection : AbstractKotlinInspection() {
if (ReplaceWithFilterFix.isApplicable(binaryExpression, leftDefaultType, context)) { if (ReplaceWithFilterFix.isApplicable(binaryExpression, leftDefaultType, context)) {
fixes.add(ReplaceWithFilterFix()) fixes.add(ReplaceWithFilterFix())
} }
if (ReplaceWithAssignmentFix.isApplicable(binaryExpression, property, context)) { when {
fixes.add(ReplaceWithAssignmentFix()) ReplaceWithAssignmentFix.isApplicable(binaryExpression, property, context) -> fixes.add(ReplaceWithAssignmentFix())
} JoinWithInitializerFix.isApplicable(binaryExpression, property) -> fixes.add(JoinWithInitializerFix(operationToken))
if (JoinWithInitializerFix.isApplicable(binaryExpression, property)) { else -> fixes.add(IntentionWrapper(ReplaceWithOrdinaryAssignmentIntention(), binaryExpression.containingKtFile))
fixes.add(JoinWithInitializerFix(operationToken))
} }
val typeText = leftDefaultType.toString().takeWhile { it != '<' }.toLowerCase() val typeText = leftDefaultType.toString().takeWhile { it != '<' }.toLowerCase()
@@ -150,7 +144,7 @@ class SuspiciousCollectionReassignmentInspection : AbstractKotlinInspection() {
} }
private class ReplaceWithAssignmentFix : LocalQuickFix { private class ReplaceWithAssignmentFix : LocalQuickFix {
override fun getName() = "Replace with assignment" override fun getName() = "Replace with assignment (original is empty)"
override fun getFamilyName() = name override fun getFamilyName() = name
@@ -1,4 +1,4 @@
// "Replace with assignment" "false" // "Replace with assignment (original is empty)" "false"
// TOOL: org.jetbrains.kotlin.idea.inspections.SuspiciousCollectionReassignmentInspection // TOOL: org.jetbrains.kotlin.idea.inspections.SuspiciousCollectionReassignmentInspection
// ACTION: Change type to mutable // ACTION: Change type to mutable
// ACTION: Replace overloaded operator with function call // ACTION: Replace overloaded operator with function call
@@ -1,4 +1,4 @@
// "Replace with assignment" "true" // "Replace with assignment (original is empty)" "true"
// TOOL: org.jetbrains.kotlin.idea.inspections.SuspiciousCollectionReassignmentInspection // TOOL: org.jetbrains.kotlin.idea.inspections.SuspiciousCollectionReassignmentInspection
// WITH_RUNTIME // WITH_RUNTIME
fun test(otherList: List<Int>) { fun test(otherList: List<Int>) {
@@ -1,4 +1,4 @@
// "Replace with assignment" "true" // "Replace with assignment (original is empty)" "true"
// TOOL: org.jetbrains.kotlin.idea.inspections.SuspiciousCollectionReassignmentInspection // TOOL: org.jetbrains.kotlin.idea.inspections.SuspiciousCollectionReassignmentInspection
// WITH_RUNTIME // WITH_RUNTIME
fun test(otherList: List<Int>) { fun test(otherList: List<Int>) {
@@ -1,4 +1,4 @@
// "Replace with assignment" "true" // "Replace with assignment (original is empty)" "true"
// TOOL: org.jetbrains.kotlin.idea.inspections.SuspiciousCollectionReassignmentInspection // TOOL: org.jetbrains.kotlin.idea.inspections.SuspiciousCollectionReassignmentInspection
// WITH_RUNTIME // WITH_RUNTIME
fun test(otherList: List<Int>) { fun test(otherList: List<Int>) {
@@ -1,4 +1,4 @@
// "Replace with assignment" "true" // "Replace with assignment (original is empty)" "true"
// TOOL: org.jetbrains.kotlin.idea.inspections.SuspiciousCollectionReassignmentInspection // TOOL: org.jetbrains.kotlin.idea.inspections.SuspiciousCollectionReassignmentInspection
// WITH_RUNTIME // WITH_RUNTIME
fun test(otherList: List<Int>) { fun test(otherList: List<Int>) {
@@ -1,4 +1,4 @@
// "Replace with assignment" "true" // "Replace with assignment (original is empty)" "true"
// TOOL: org.jetbrains.kotlin.idea.inspections.SuspiciousCollectionReassignmentInspection // TOOL: org.jetbrains.kotlin.idea.inspections.SuspiciousCollectionReassignmentInspection
// WITH_RUNTIME // WITH_RUNTIME
fun test(otherMap: Map<Int, Int>) { fun test(otherMap: Map<Int, Int>) {
@@ -1,4 +1,4 @@
// "Replace with assignment" "true" // "Replace with assignment (original is empty)" "true"
// TOOL: org.jetbrains.kotlin.idea.inspections.SuspiciousCollectionReassignmentInspection // TOOL: org.jetbrains.kotlin.idea.inspections.SuspiciousCollectionReassignmentInspection
// WITH_RUNTIME // WITH_RUNTIME
fun test(otherMap: Map<Int, Int>) { fun test(otherMap: Map<Int, Int>) {
@@ -1,4 +1,4 @@
// "Replace with assignment" "true" // "Replace with assignment (original is empty)" "true"
// TOOL: org.jetbrains.kotlin.idea.inspections.SuspiciousCollectionReassignmentInspection // TOOL: org.jetbrains.kotlin.idea.inspections.SuspiciousCollectionReassignmentInspection
// WITH_RUNTIME // WITH_RUNTIME
fun test(otherMap: Map<Int, Int>) { fun test(otherMap: Map<Int, Int>) {
@@ -1,4 +1,4 @@
// "Replace with assignment" "true" // "Replace with assignment (original is empty)" "true"
// TOOL: org.jetbrains.kotlin.idea.inspections.SuspiciousCollectionReassignmentInspection // TOOL: org.jetbrains.kotlin.idea.inspections.SuspiciousCollectionReassignmentInspection
// WITH_RUNTIME // WITH_RUNTIME
fun test(otherMap: Map<Int, Int>) { fun test(otherMap: Map<Int, Int>) {
@@ -1,4 +1,4 @@
// "Replace with assignment" "true" // "Replace with assignment (original is empty)" "true"
// TOOL: org.jetbrains.kotlin.idea.inspections.SuspiciousCollectionReassignmentInspection // TOOL: org.jetbrains.kotlin.idea.inspections.SuspiciousCollectionReassignmentInspection
// WITH_RUNTIME // WITH_RUNTIME
fun test(otherList: Set<Int>) { fun test(otherList: Set<Int>) {
@@ -1,4 +1,4 @@
// "Replace with assignment" "true" // "Replace with assignment (original is empty)" "true"
// TOOL: org.jetbrains.kotlin.idea.inspections.SuspiciousCollectionReassignmentInspection // TOOL: org.jetbrains.kotlin.idea.inspections.SuspiciousCollectionReassignmentInspection
// WITH_RUNTIME // WITH_RUNTIME
fun test(otherList: Set<Int>) { fun test(otherList: Set<Int>) {
@@ -1,4 +1,4 @@
// "Replace with assignment" "true" // "Replace with assignment (original is empty)" "true"
// TOOL: org.jetbrains.kotlin.idea.inspections.SuspiciousCollectionReassignmentInspection // TOOL: org.jetbrains.kotlin.idea.inspections.SuspiciousCollectionReassignmentInspection
// WITH_RUNTIME // WITH_RUNTIME
fun test(otherList: Set<Int>) { fun test(otherList: Set<Int>) {
@@ -1,4 +1,4 @@
// "Replace with assignment" "true" // "Replace with assignment (original is empty)" "true"
// TOOL: org.jetbrains.kotlin.idea.inspections.SuspiciousCollectionReassignmentInspection // TOOL: org.jetbrains.kotlin.idea.inspections.SuspiciousCollectionReassignmentInspection
// WITH_RUNTIME // WITH_RUNTIME
fun test(otherList: Set<Int>) { fun test(otherList: Set<Int>) {
@@ -1,4 +1,4 @@
// "Replace with assignment" "false" // "Replace with assignment (original is empty)" "false"
// TOOL: org.jetbrains.kotlin.idea.inspections.SuspiciousCollectionReassignmentInspection // TOOL: org.jetbrains.kotlin.idea.inspections.SuspiciousCollectionReassignmentInspection
// ACTION: Change type to mutable // ACTION: Change type to mutable
// ACTION: Replace overloaded operator with function call // ACTION: Replace overloaded operator with function call
@@ -1,4 +1,4 @@
// "Replace with assignment" "false" // "Replace with assignment (original is empty)" "false"
// TOOL: org.jetbrains.kotlin.idea.inspections.SuspiciousCollectionReassignmentInspection // TOOL: org.jetbrains.kotlin.idea.inspections.SuspiciousCollectionReassignmentInspection
// ACTION: Change type to mutable // ACTION: Change type to mutable
// ACTION: Replace overloaded operator with function call // ACTION: Replace overloaded operator with function call
@@ -1,4 +1,4 @@
// "Replace with assignment" "false" // "Replace with assignment (original is empty)" "false"
// TOOL: org.jetbrains.kotlin.idea.inspections.SuspiciousCollectionReassignmentInspection // TOOL: org.jetbrains.kotlin.idea.inspections.SuspiciousCollectionReassignmentInspection
// ACTION: Change type to mutable // ACTION: Change type to mutable
// ACTION: Replace overloaded operator with function call // ACTION: Replace overloaded operator with function call
@@ -1,4 +1,4 @@
// "Replace with assignment" "false" // "Replace with assignment (original is empty)" "false"
// ACTION: Replace overloaded operator with function call // ACTION: Replace overloaded operator with function call
// ACTION: Replace with ordinary assignment // ACTION: Replace with ordinary assignment
// WITH_RUNTIME // WITH_RUNTIME
@@ -1,4 +1,4 @@
// "Replace with assignment" "false" // "Replace with assignment (original is empty)" "false"
// TOOL: org.jetbrains.kotlin.idea.inspections.SuspiciousCollectionReassignmentInspection // TOOL: org.jetbrains.kotlin.idea.inspections.SuspiciousCollectionReassignmentInspection
// ACTION: Change type to mutable // ACTION: Change type to mutable
// ACTION: Replace overloaded operator with function call // ACTION: Replace overloaded operator with function call
@@ -1,4 +1,4 @@
// "Replace with assignment" "false" // "Replace with assignment (original is empty)" "false"
// ACTION: Replace overloaded operator with function call // ACTION: Replace overloaded operator with function call
// ACTION: Replace with ordinary assignment // ACTION: Replace with ordinary assignment
// WITH_RUNTIME // WITH_RUNTIME
@@ -1,4 +1,4 @@
// "Replace with assignment" "false" // "Replace with assignment (original is empty)" "false"
// TOOL: org.jetbrains.kotlin.idea.inspections.SuspiciousCollectionReassignmentInspection // TOOL: org.jetbrains.kotlin.idea.inspections.SuspiciousCollectionReassignmentInspection
// ACTION: Change type to mutable // ACTION: Change type to mutable
// ACTION: Replace overloaded operator with function call // ACTION: Replace overloaded operator with function call
@@ -1,4 +1,4 @@
// "Replace with assignment" "false" // "Replace with assignment (original is empty)" "false"
// TOOL: org.jetbrains.kotlin.idea.inspections.SuspiciousCollectionReassignmentInspection // TOOL: org.jetbrains.kotlin.idea.inspections.SuspiciousCollectionReassignmentInspection
// ACTION: Change type to mutable // ACTION: Change type to mutable
// ACTION: Replace overloaded operator with function call // ACTION: Replace overloaded operator with function call
@@ -0,0 +1,10 @@
// "Replace with ordinary assignment" "true"
// TOOL: org.jetbrains.kotlin.idea.inspections.SuspiciousCollectionReassignmentInspection
// ACTION: Replace overloaded operator with function call
// WITH_RUNTIME
fun test(otherList: List<Int>, flag: Boolean) {
var list = otherList
if (flag) {
list <caret>+= 4
}
}
@@ -0,0 +1,10 @@
// "Replace with ordinary assignment" "true"
// TOOL: org.jetbrains.kotlin.idea.inspections.SuspiciousCollectionReassignmentInspection
// ACTION: Replace overloaded operator with function call
// WITH_RUNTIME
fun test(otherList: List<Int>, flag: Boolean) {
var list = otherList
if (flag) {
list = list + 4
}
}
@@ -4022,6 +4022,19 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/suspiciousCollectionReassignment/replaceWithFilter"), Pattern.compile("^(\\w+)\\.((before\\.Main\\.\\w+)|(test))$"), TargetBackend.ANY, true); KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/suspiciousCollectionReassignment/replaceWithFilter"), Pattern.compile("^(\\w+)\\.((before\\.Main\\.\\w+)|(test))$"), TargetBackend.ANY, true);
} }
} }
@TestMetadata("idea/testData/quickfix/suspiciousCollectionReassignment/replaceWithOrdinaryAssignment")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class ReplaceWithOrdinaryAssignment extends AbstractQuickFixMultiFileTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTestWithExtraFile, TargetBackend.ANY, testDataFilePath);
}
public void testAllFilesPresentInReplaceWithOrdinaryAssignment() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/suspiciousCollectionReassignment/replaceWithOrdinaryAssignment"), Pattern.compile("^(\\w+)\\.((before\\.Main\\.\\w+)|(test))$"), TargetBackend.ANY, true);
}
}
} }
@TestMetadata("idea/testData/quickfix/toString") @TestMetadata("idea/testData/quickfix/toString")
@@ -11582,6 +11582,24 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
runTest("idea/testData/quickfix/suspiciousCollectionReassignment/replaceWithFilter/plusEq.kt"); runTest("idea/testData/quickfix/suspiciousCollectionReassignment/replaceWithFilter/plusEq.kt");
} }
} }
@TestMetadata("idea/testData/quickfix/suspiciousCollectionReassignment/replaceWithOrdinaryAssignment")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class ReplaceWithOrdinaryAssignment extends AbstractQuickFixTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
}
public void testAllFilesPresentInReplaceWithOrdinaryAssignment() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/suspiciousCollectionReassignment/replaceWithOrdinaryAssignment"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true);
}
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
runTest("idea/testData/quickfix/suspiciousCollectionReassignment/replaceWithOrdinaryAssignment/simple.kt");
}
}
} }
@TestMetadata("idea/testData/quickfix/toString") @TestMetadata("idea/testData/quickfix/toString")