Lift assignment: do not report in cases where argument are of different types #KT-21520 Fixed
This commit is contained in:
committed by
Mikhail Glukhikh
parent
fcc6395b14
commit
0cd25353bc
+20
-4
@@ -17,6 +17,7 @@
|
|||||||
package org.jetbrains.kotlin.idea.intentions.branchedTransformations
|
package org.jetbrains.kotlin.idea.intentions.branchedTransformations
|
||||||
|
|
||||||
import org.jetbrains.kotlin.cfg.WhenChecker
|
import org.jetbrains.kotlin.cfg.WhenChecker
|
||||||
|
import org.jetbrains.kotlin.diagnostics.Errors
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
import org.jetbrains.kotlin.idea.intentions.branches
|
import org.jetbrains.kotlin.idea.intentions.branches
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
@@ -25,6 +26,8 @@ import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType
|
|||||||
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.lastBlockStatementOrThis
|
import org.jetbrains.kotlin.psi.psiUtil.lastBlockStatementOrThis
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.callUtil.getType
|
||||||
|
import org.jetbrains.kotlin.types.TypeConstructor
|
||||||
import org.jetbrains.kotlin.types.typeUtil.isNothing
|
import org.jetbrains.kotlin.types.typeUtil.isNothing
|
||||||
|
|
||||||
object BranchedFoldingUtils {
|
object BranchedFoldingUtils {
|
||||||
@@ -52,8 +55,20 @@ object BranchedFoldingUtils {
|
|||||||
it.getTargetLabel() == null
|
it.getTargetLabel() == null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkAssignmentsMatch(a1: KtBinaryExpression, a2: KtBinaryExpression): Boolean =
|
private fun KtBinaryExpression.checkAssignmentsMatch(other: KtBinaryExpression, rightTypeConstructor: TypeConstructor): Boolean {
|
||||||
a1.left?.text == a2.left?.text && a1.operationToken == a2.operationToken
|
return left?.text == other.left?.text
|
||||||
|
&& operationToken == other.operationToken
|
||||||
|
&& rightTypeConstructor == other.rightTypeConstructor()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KtBinaryExpression.rightTypeConstructor(): TypeConstructor? {
|
||||||
|
val right = this.right ?: return null
|
||||||
|
val context = this.analyze()
|
||||||
|
val diagnostics = context.diagnostics
|
||||||
|
fun hasTypeMismatchError(e: KtExpression) = diagnostics.forElement(e).any { it.factory == Errors.TYPE_MISMATCH }
|
||||||
|
if (hasTypeMismatchError(this) || hasTypeMismatchError(right)) return null
|
||||||
|
return right.getType(context)?.constructor
|
||||||
|
}
|
||||||
|
|
||||||
internal fun getFoldableAssignmentNumber(expression: KtExpression?): Int {
|
internal fun getFoldableAssignmentNumber(expression: KtExpression?): Int {
|
||||||
expression ?: return -1
|
expression ?: return -1
|
||||||
@@ -89,14 +104,15 @@ object BranchedFoldingUtils {
|
|||||||
}
|
}
|
||||||
if (!collectAssignmentsAndCheck(expression)) return -1
|
if (!collectAssignmentsAndCheck(expression)) return -1
|
||||||
val firstAssignment = assignments.firstOrNull() ?: return 0
|
val firstAssignment = assignments.firstOrNull() ?: return 0
|
||||||
if (assignments.any { !BranchedFoldingUtils.checkAssignmentsMatch(it, firstAssignment) }) {
|
val rightTypeConstructor = firstAssignment.rightTypeConstructor() ?: return -1
|
||||||
|
if (assignments.any { !firstAssignment.checkAssignmentsMatch(it, rightTypeConstructor) }) {
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
if (expression.anyDescendantOfType<KtBinaryExpression>(
|
if (expression.anyDescendantOfType<KtBinaryExpression>(
|
||||||
predicate = {
|
predicate = {
|
||||||
if (it.operationToken in KtTokens.ALL_ASSIGNMENTS)
|
if (it.operationToken in KtTokens.ALL_ASSIGNMENTS)
|
||||||
if (it.getNonStrictParentOfType<KtFinallySection>() != null)
|
if (it.getNonStrictParentOfType<KtFinallySection>() != null)
|
||||||
BranchedFoldingUtils.checkAssignmentsMatch(it, firstAssignment)
|
firstAssignment.checkAssignmentsMatch(it, rightTypeConstructor)
|
||||||
else
|
else
|
||||||
it !in assignments
|
it !in assignments
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun test(b: Boolean, x: Int, y: Int?) {
|
||||||
|
val list = mutableListOf<Int?>()
|
||||||
|
<caret>if (b) {
|
||||||
|
list += x
|
||||||
|
} else {
|
||||||
|
list += y
|
||||||
|
}
|
||||||
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun test(b: Boolean, x: Int, y: Int?) {
|
||||||
|
val list = mutableListOf<Int?>()
|
||||||
|
list += if (b) {
|
||||||
|
x
|
||||||
|
} else {
|
||||||
|
y
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// PROBLEM: none
|
||||||
|
fun test(b: Boolean, x: Long, y: Int) {
|
||||||
|
var num: Long = 0L
|
||||||
|
<caret>if (b) {
|
||||||
|
num += x
|
||||||
|
} else {
|
||||||
|
num += y
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// PROBLEM: none
|
||||||
|
// ERROR: Type mismatch: inferred type is Long? but Long was expected
|
||||||
|
fun test(b: Boolean, x: Long, y: Long?) {
|
||||||
|
var num: Long = 0L
|
||||||
|
<caret>if (b) {
|
||||||
|
num += x
|
||||||
|
} else {
|
||||||
|
num += y
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
// PROBLEM: none
|
||||||
|
class C {
|
||||||
|
operator fun plusAssign(i: Int) {}
|
||||||
|
operator fun plusAssign(b: Boolean) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun f(b: Boolean) {
|
||||||
|
val c = C()
|
||||||
|
<caret>if (b)
|
||||||
|
c += 1
|
||||||
|
else
|
||||||
|
c += true
|
||||||
|
}
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
// PROBLEM: none
|
||||||
|
// WITH_RUNTIME
|
||||||
|
fun test(b: Boolean) {
|
||||||
|
val list = mutableListOf<Int>()
|
||||||
|
<caret>if (b) {
|
||||||
|
list += 1
|
||||||
|
} else {
|
||||||
|
list += mutableListOf(2)
|
||||||
|
}
|
||||||
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
// PROBLEM: none
|
||||||
|
// ERROR: Type mismatch: inferred type is List<Any> but MutableList<Int> was expected
|
||||||
|
// ERROR: Val cannot be reassigned
|
||||||
|
// WITH_RUNTIME
|
||||||
|
fun test(b: Boolean) {
|
||||||
|
val list = mutableListOf<Int>()
|
||||||
|
<caret>if (b) {
|
||||||
|
list += mutableListOf(1)
|
||||||
|
} else {
|
||||||
|
list += mutableListOf(2L)
|
||||||
|
}
|
||||||
|
}
|
||||||
+30
@@ -2752,6 +2752,11 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
|
|||||||
runTest("idea/testData/inspectionsLocal/liftOut/ifToAssignment/multipleAssignments.kt");
|
runTest("idea/testData/inspectionsLocal/liftOut/ifToAssignment/multipleAssignments.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("nullableMutableList.kt")
|
||||||
|
public void testNullableMutableList() throws Exception {
|
||||||
|
runTest("idea/testData/inspectionsLocal/liftOut/ifToAssignment/nullableMutableList.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("simpleIf.kt")
|
@TestMetadata("simpleIf.kt")
|
||||||
public void testSimpleIf() throws Exception {
|
public void testSimpleIf() throws Exception {
|
||||||
runTest("idea/testData/inspectionsLocal/liftOut/ifToAssignment/simpleIf.kt");
|
runTest("idea/testData/inspectionsLocal/liftOut/ifToAssignment/simpleIf.kt");
|
||||||
@@ -2791,6 +2796,31 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
|
|||||||
public void testSimpleIfWithoutTerminatingAssignment() throws Exception {
|
public void testSimpleIfWithoutTerminatingAssignment() throws Exception {
|
||||||
runTest("idea/testData/inspectionsLocal/liftOut/ifToAssignment/simpleIfWithoutTerminatingAssignment.kt");
|
runTest("idea/testData/inspectionsLocal/liftOut/ifToAssignment/simpleIfWithoutTerminatingAssignment.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("typeMismatch.kt")
|
||||||
|
public void testTypeMismatch() throws Exception {
|
||||||
|
runTest("idea/testData/inspectionsLocal/liftOut/ifToAssignment/typeMismatch.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("typeMismatch2.kt")
|
||||||
|
public void testTypeMismatch2() throws Exception {
|
||||||
|
runTest("idea/testData/inspectionsLocal/liftOut/ifToAssignment/typeMismatch2.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("typeMismatch3.kt")
|
||||||
|
public void testTypeMismatch3() throws Exception {
|
||||||
|
runTest("idea/testData/inspectionsLocal/liftOut/ifToAssignment/typeMismatch3.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("typeMismatchMutableList.kt")
|
||||||
|
public void testTypeMismatchMutableList() throws Exception {
|
||||||
|
runTest("idea/testData/inspectionsLocal/liftOut/ifToAssignment/typeMismatchMutableList.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("typeMismatchMutableList2.kt")
|
||||||
|
public void testTypeMismatchMutableList2() throws Exception {
|
||||||
|
runTest("idea/testData/inspectionsLocal/liftOut/ifToAssignment/typeMismatchMutableList2.kt");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("idea/testData/inspectionsLocal/liftOut/ifToReturn")
|
@TestMetadata("idea/testData/inspectionsLocal/liftOut/ifToReturn")
|
||||||
|
|||||||
Reference in New Issue
Block a user