Refactoring: "replace with operator assignment" is now an inspection
This commit is contained in:
-4
@@ -1,4 +0,0 @@
|
||||
fun foo(x: Int) {
|
||||
var y = 1
|
||||
y += x
|
||||
}
|
||||
-4
@@ -1,4 +0,0 @@
|
||||
fun foo(x: Int) {
|
||||
var y = 1
|
||||
<spot>y = y + x</spot>
|
||||
}
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
<html>
|
||||
<body>
|
||||
This intention transforms a modification of a variable with a simple assignment into an operation-assignment.
|
||||
</body>
|
||||
</html>
|
||||
@@ -1072,11 +1072,6 @@
|
||||
<category>Kotlin</category>
|
||||
</intentionAction>
|
||||
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.idea.intentions.ReplaceWithOperatorAssignmentIntention</className>
|
||||
<category>Kotlin</category>
|
||||
</intentionAction>
|
||||
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.idea.intentions.ReplaceWithOrdinaryAssignmentIntention</className>
|
||||
<category>Kotlin</category>
|
||||
@@ -1656,7 +1651,7 @@
|
||||
language="kotlin"
|
||||
/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.ReplaceWithOperatorAssignmentInspection"
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.ReplaceWithOperatorAssignmentInspection"
|
||||
displayName="Assignment that can be replaced with operator assignment"
|
||||
groupPath="Kotlin"
|
||||
groupName="Style issues"
|
||||
|
||||
+41
-19
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
* 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.
|
||||
@@ -14,28 +14,34 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.intentions
|
||||
package org.jetbrains.kotlin.idea.inspections
|
||||
|
||||
import com.intellij.codeInspection.LocalInspectionToolSession
|
||||
import com.intellij.codeInspection.ProblemsHolder
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.tree.IElementType
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.idea.analysis.analyzeAsReplacement
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.inspections.IntentionBasedInspection
|
||||
import org.jetbrains.kotlin.idea.util.psi.patternMatching.matches
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.KtBinaryExpression
|
||||
import org.jetbrains.kotlin.psi.KtNameReferenceExpression
|
||||
import org.jetbrains.kotlin.psi.KtPsiFactory
|
||||
import org.jetbrains.kotlin.psi.doNotAnalyze
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
|
||||
class ReplaceWithOperatorAssignmentInspection : IntentionBasedInspection<KtBinaryExpression>(ReplaceWithOperatorAssignmentIntention::class)
|
||||
class ReplaceWithOperatorAssignmentInspection : AbstractApplicabilityBasedInspection<KtBinaryExpression>() {
|
||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): KtVisitorVoid =
|
||||
object : KtVisitorVoid() {
|
||||
override fun visitBinaryExpression(expression: KtBinaryExpression) {
|
||||
super.visitBinaryExpression(expression)
|
||||
visitTargetElement(expression, holder, isOnTheFly)
|
||||
}
|
||||
}
|
||||
|
||||
class ReplaceWithOperatorAssignmentIntention : SelfTargetingOffsetIndependentIntention<KtBinaryExpression>(KtBinaryExpression::class.java, "Replace with operator-assignment") {
|
||||
override fun isApplicableTo(element: KtBinaryExpression): Boolean {
|
||||
override fun isApplicable(element: KtBinaryExpression): Boolean {
|
||||
if (element.operationToken != KtTokens.EQ) return false
|
||||
val left = element.left as? KtNameReferenceExpression ?: return false
|
||||
val right = element.right as? KtBinaryExpression ?: return false
|
||||
@@ -51,13 +57,18 @@ class ReplaceWithOperatorAssignmentIntention : SelfTargetingOffsetIndependentInt
|
||||
return newBindingContext.diagnostics.forElement(opAssign.operationReference).isEmpty()
|
||||
}
|
||||
|
||||
override fun inspectionText(element: KtBinaryExpression) = "Replaceable with operator-assignment"
|
||||
|
||||
override val defaultFixText = "Replace with operator-assignment"
|
||||
|
||||
override fun fixText(element: KtBinaryExpression) =
|
||||
"Replace with ${element.operationReference.operationSignTokenType?.value}= expression"
|
||||
|
||||
private fun checkExpressionRepeat(variableExpression: KtNameReferenceExpression, expression: KtBinaryExpression, bindingContext: BindingContext): Boolean {
|
||||
val descriptor = bindingContext[BindingContext.REFERENCE_TARGET, expression.operationReference]?.containingDeclaration
|
||||
val isPrimitiveOperation = descriptor is ClassDescriptor && KotlinBuiltIns.isPrimitiveType(descriptor.defaultType)
|
||||
|
||||
val operationToken = expression.operationToken
|
||||
text = "Replace with ${expression.operationReference.text}= expression"
|
||||
|
||||
val expressionLeft = expression.left
|
||||
val expressionRight = expression.right
|
||||
return when {
|
||||
@@ -87,8 +98,8 @@ class ReplaceWithOperatorAssignmentIntention : SelfTargetingOffsetIndependentInt
|
||||
operationToken == KtTokens.DIV ||
|
||||
operationToken == KtTokens.PERC
|
||||
|
||||
override fun applyTo(element: KtBinaryExpression, editor: Editor?) {
|
||||
element.replace(buildOperatorAssignment(element))
|
||||
override fun applyTo(element: PsiElement, project: Project, editor: Editor?) {
|
||||
(element as? KtBinaryExpression)?.replace(buildOperatorAssignment(element))
|
||||
}
|
||||
|
||||
private fun buildOperatorAssignment(element: KtBinaryExpression): KtBinaryExpression {
|
||||
@@ -100,21 +111,32 @@ class ReplaceWithOperatorAssignmentIntention : SelfTargetingOffsetIndependentInt
|
||||
return KtPsiFactory(element).createExpression(replacement) as KtBinaryExpression
|
||||
}
|
||||
|
||||
tailrec private fun buildOperatorAssignmentText(variableExpression: KtNameReferenceExpression, expression: KtBinaryExpression, tail: String): String {
|
||||
private tailrec fun buildOperatorAssignmentText(
|
||||
variableExpression: KtNameReferenceExpression,
|
||||
expression: KtBinaryExpression,
|
||||
tail: String
|
||||
): String {
|
||||
val operationText = expression.operationReference.text
|
||||
val variableName = variableExpression.text
|
||||
|
||||
fun String.appendTail() = if (tail.isEmpty()) this else "$this $tail"
|
||||
|
||||
return when {
|
||||
variableExpression.matches(expression.left) -> "$variableName $operationText= ${expression.right!!.text}".appendTail()
|
||||
variableExpression.matches(expression.left) ->
|
||||
"$variableName $operationText= ${expression.right!!.text}".appendTail()
|
||||
|
||||
variableExpression.matches(expression.right) -> "$variableName $operationText= ${expression.left!!.text}".appendTail()
|
||||
variableExpression.matches(expression.right) ->
|
||||
"$variableName $operationText= ${expression.left!!.text}".appendTail()
|
||||
|
||||
expression.left is KtBinaryExpression ->
|
||||
buildOperatorAssignmentText(variableExpression, expression.left as KtBinaryExpression, "$operationText ${expression.right!!.text}".appendTail())
|
||||
buildOperatorAssignmentText(
|
||||
variableExpression,
|
||||
expression.left as KtBinaryExpression,
|
||||
"$operationText ${expression.right!!.text}".appendTail()
|
||||
)
|
||||
|
||||
else -> tail
|
||||
else ->
|
||||
tail
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
org.jetbrains.kotlin.idea.inspections.ReplaceWithOperatorAssignmentInspection
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// IS_APPLICABLE: false
|
||||
// PROBLEM: none
|
||||
// WITH_RUNTIME
|
||||
fun foo() {
|
||||
var list1 = java.util.Collections.emptyList<String>()
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// IS_APPLICABLE: false
|
||||
// PROBLEM: none
|
||||
fun foo() {
|
||||
var x = 0
|
||||
x =<caret> x / 1 + 1
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// IS_APPLICABLE: false
|
||||
// PROBLEM: none
|
||||
fun foo() {
|
||||
var x = 0
|
||||
val y = 0
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// IS_APPLICABLE: false
|
||||
// PROBLEM: none
|
||||
fun foo() {
|
||||
var x = 0
|
||||
<caret>x = 1 - x
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// IS_APPLICABLE: false
|
||||
// PROBLEM: none
|
||||
fun foo() {
|
||||
var x = 0
|
||||
x =<caret> x - 1 - 1
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// IS_APPLICABLE: false
|
||||
// PROBLEM: none
|
||||
fun foo() {
|
||||
var x = 0
|
||||
val y = 0
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// IS_APPLICABLE: false
|
||||
// PROBLEM: none
|
||||
class A
|
||||
|
||||
operator fun A.plus(a: A): A = A()
|
||||
@@ -1 +0,0 @@
|
||||
org.jetbrains.kotlin.idea.intentions.ReplaceWithOperatorAssignmentIntention
|
||||
@@ -3294,6 +3294,87 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/inspectionsLocal/replaceWithOperatorAssignment")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class ReplaceWithOperatorAssignment extends AbstractLocalInspectionTest {
|
||||
public void testAllFilesPresentInReplaceWithOperatorAssignment() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/inspectionsLocal/replaceWithOperatorAssignment"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("flexibleTypeBug.kt")
|
||||
public void testFlexibleTypeBug() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/replaceWithOperatorAssignment/flexibleTypeBug.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("illegalMultipleOperators.kt")
|
||||
public void testIllegalMultipleOperators() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/replaceWithOperatorAssignment/illegalMultipleOperators.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("illegalMultipleOperatorsMiddle.kt")
|
||||
public void testIllegalMultipleOperatorsMiddle() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/replaceWithOperatorAssignment/illegalMultipleOperatorsMiddle.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("invalidSubtraction.kt")
|
||||
public void testInvalidSubtraction() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/replaceWithOperatorAssignment/invalidSubtraction.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("multipleOperators.kt")
|
||||
public void testMultipleOperators() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/replaceWithOperatorAssignment/multipleOperators.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("multipleOperatorsRightSideRepeat.kt")
|
||||
public void testMultipleOperatorsRightSideRepeat() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/replaceWithOperatorAssignment/multipleOperatorsRightSideRepeat.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("nonCommutativeRepeat.kt")
|
||||
public void testNonCommutativeRepeat() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/replaceWithOperatorAssignment/nonCommutativeRepeat.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("nonRepeatingAssignment.kt")
|
||||
public void testNonRepeatingAssignment() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/replaceWithOperatorAssignment/nonRepeatingAssignment.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("plusAssignConflict.kt")
|
||||
public void testPlusAssignConflict() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/replaceWithOperatorAssignment/plusAssignConflict.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("rightSideRepeat.kt")
|
||||
public void testRightSideRepeat() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/replaceWithOperatorAssignment/rightSideRepeat.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("simpleAssign.kt")
|
||||
public void testSimpleAssign() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/replaceWithOperatorAssignment/simpleAssign.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("validSubtraction.kt")
|
||||
public void testValidSubtraction() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/replaceWithOperatorAssignment/validSubtraction.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/inspectionsLocal/selfAssignment")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
@@ -14154,87 +14154,6 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/intentions/replaceWithOperatorAssignment")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class ReplaceWithOperatorAssignment extends AbstractIntentionTest {
|
||||
public void testAllFilesPresentInReplaceWithOperatorAssignment() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/replaceWithOperatorAssignment"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("flexibleTypeBug.kt")
|
||||
public void testFlexibleTypeBug() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/replaceWithOperatorAssignment/flexibleTypeBug.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("illegalMultipleOperators.kt")
|
||||
public void testIllegalMultipleOperators() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/replaceWithOperatorAssignment/illegalMultipleOperators.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("illegalMultipleOperatorsMiddle.kt")
|
||||
public void testIllegalMultipleOperatorsMiddle() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/replaceWithOperatorAssignment/illegalMultipleOperatorsMiddle.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("invalidSubtraction.kt")
|
||||
public void testInvalidSubtraction() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/replaceWithOperatorAssignment/invalidSubtraction.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("multipleOperators.kt")
|
||||
public void testMultipleOperators() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/replaceWithOperatorAssignment/multipleOperators.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("multipleOperatorsRightSideRepeat.kt")
|
||||
public void testMultipleOperatorsRightSideRepeat() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/replaceWithOperatorAssignment/multipleOperatorsRightSideRepeat.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("nonCommutativeRepeat.kt")
|
||||
public void testNonCommutativeRepeat() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/replaceWithOperatorAssignment/nonCommutativeRepeat.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("nonRepeatingAssignment.kt")
|
||||
public void testNonRepeatingAssignment() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/replaceWithOperatorAssignment/nonRepeatingAssignment.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("plusAssignConflict.kt")
|
||||
public void testPlusAssignConflict() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/replaceWithOperatorAssignment/plusAssignConflict.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("rightSideRepeat.kt")
|
||||
public void testRightSideRepeat() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/replaceWithOperatorAssignment/rightSideRepeat.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("simpleAssign.kt")
|
||||
public void testSimpleAssign() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/replaceWithOperatorAssignment/simpleAssign.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("validSubtraction.kt")
|
||||
public void testValidSubtraction() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/replaceWithOperatorAssignment/validSubtraction.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/intentions/replaceWithOrdinaryAssignment")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Reference in New Issue
Block a user