Lift assignment: suggest for assignment of null
#KT-30191 Fixed #KT-28595 Fixed
This commit is contained in:
committed by
Dmitry Gridin
parent
4b4be07942
commit
05a132357b
+20
-10
@@ -28,8 +28,11 @@ import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.lastBlockStatementOrThis
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getType
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeConstructor
|
||||
import org.jetbrains.kotlin.types.typeUtil.isNothing
|
||||
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||
|
||||
object BranchedFoldingUtils {
|
||||
private fun getFoldableBranchedAssignment(branch: KtExpression?): KtBinaryExpression? {
|
||||
@@ -56,19 +59,25 @@ object BranchedFoldingUtils {
|
||||
it.getTargetLabel() == null
|
||||
}
|
||||
|
||||
private fun KtBinaryExpression.checkAssignmentsMatch(other: KtBinaryExpression, rightTypeConstructor: TypeConstructor): Boolean {
|
||||
return left?.text == other.left?.text
|
||||
&& operationToken == other.operationToken
|
||||
&& rightTypeConstructor == other.rightTypeConstructor()
|
||||
private fun KtBinaryExpression.checkAssignmentsMatch(
|
||||
other: KtBinaryExpression,
|
||||
leftType: KotlinType,
|
||||
rightTypeConstructor: TypeConstructor
|
||||
): Boolean {
|
||||
val left = this.left ?: return false
|
||||
val otherLeft = other.left ?: return false
|
||||
if (left.text != otherLeft.text || operationToken != other.operationToken) return false
|
||||
val rightType = other.rightType() ?: return false
|
||||
return rightType.constructor == rightTypeConstructor || (operationToken == KtTokens.EQ && rightType.isSubtypeOf(leftType))
|
||||
}
|
||||
|
||||
private fun KtBinaryExpression.rightTypeConstructor(): TypeConstructor? {
|
||||
private fun KtBinaryExpression.rightType(): KotlinType? {
|
||||
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
|
||||
return right.getType(context)
|
||||
}
|
||||
|
||||
internal fun getFoldableAssignmentNumber(expression: KtExpression?): Int {
|
||||
@@ -104,16 +113,17 @@ object BranchedFoldingUtils {
|
||||
else -> false
|
||||
}
|
||||
if (!collectAssignmentsAndCheck(expression)) return -1
|
||||
val firstAssignment = assignments.firstOrNull() ?: return 0
|
||||
val rightTypeConstructor = firstAssignment.rightTypeConstructor() ?: return -1
|
||||
if (assignments.any { !firstAssignment.checkAssignmentsMatch(it, rightTypeConstructor) }) {
|
||||
val firstAssignment = assignments.firstOrNull { !it.right.isNullExpression() } ?: assignments.firstOrNull() ?: return 0
|
||||
val leftType = firstAssignment.left?.let { it.getType(it.analyze(BodyResolveMode.PARTIAL)) } ?: return 0
|
||||
val rightTypeConstructor = firstAssignment.rightType()?.constructor ?: return -1
|
||||
if (assignments.any { !firstAssignment.checkAssignmentsMatch(it, leftType, rightTypeConstructor) }) {
|
||||
return -1
|
||||
}
|
||||
if (expression.anyDescendantOfType<KtBinaryExpression>(
|
||||
predicate = {
|
||||
if (it.operationToken in KtTokens.ALL_ASSIGNMENTS)
|
||||
if (it.getNonStrictParentOfType<KtFinallySection>() != null)
|
||||
firstAssignment.checkAssignmentsMatch(it, rightTypeConstructor)
|
||||
firstAssignment.checkAssignmentsMatch(it, leftType, rightTypeConstructor)
|
||||
else
|
||||
it !in assignments
|
||||
else
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fun bar(i: Int) {
|
||||
var str: String? = null
|
||||
|
||||
<caret>if (i == 1) {
|
||||
str = null
|
||||
} else if (i == 2) {
|
||||
str = "2"
|
||||
} else {
|
||||
str = "3"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fun bar(i: Int) {
|
||||
var str: String? = null
|
||||
|
||||
str = if (i == 1) {
|
||||
null
|
||||
} else if (i == 2) {
|
||||
"2"
|
||||
} else {
|
||||
"3"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fun bar(i: Int) {
|
||||
var str: String? = null
|
||||
|
||||
<caret>if (i == 1) {
|
||||
str = "1"
|
||||
} else if (i == 2) {
|
||||
str = "2"
|
||||
} else {
|
||||
str = null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fun bar(i: Int) {
|
||||
var str: String? = null
|
||||
|
||||
str = if (i == 1) {
|
||||
"1"
|
||||
} else if (i == 2) {
|
||||
"2"
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fun bar(i: Int) {
|
||||
var str: String? = null
|
||||
|
||||
<caret>if (i == 1) {
|
||||
str = null
|
||||
} else if (i == 2) {
|
||||
str = null
|
||||
} else {
|
||||
str = null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fun bar(i: Int) {
|
||||
var str: String? = null
|
||||
|
||||
str = if (i == 1) {
|
||||
null
|
||||
} else if (i == 2) {
|
||||
null
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// PROBLEM: none
|
||||
// DISABLE-ERRORS
|
||||
fun bar(i: Int) {
|
||||
var str: String? = null
|
||||
|
||||
<caret>if (i == 1) {
|
||||
str = null
|
||||
} else if (i == 2) {
|
||||
str = "2"
|
||||
} else {
|
||||
str = 3
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// PROBLEM: none
|
||||
// DISABLE-ERRORS
|
||||
fun bar(i: Int) {
|
||||
var str: String = ""
|
||||
|
||||
<caret>if (i == 1) {
|
||||
str = null
|
||||
} else if (i == 2) {
|
||||
str = "2"
|
||||
} else {
|
||||
str = "3"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
open class A
|
||||
class B : A()
|
||||
class C : A()
|
||||
|
||||
fun liftClass(boolean: Boolean) {
|
||||
val a1: A
|
||||
<caret>if (boolean) {
|
||||
a1 = B()
|
||||
} else {
|
||||
a1 = C()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
open class A
|
||||
class B : A()
|
||||
class C : A()
|
||||
|
||||
fun liftClass(boolean: Boolean) {
|
||||
val a1: A
|
||||
a1 = if (boolean) {
|
||||
B()
|
||||
} else {
|
||||
C()
|
||||
}
|
||||
}
|
||||
+30
@@ -4575,6 +4575,31 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
|
||||
runTest("idea/testData/inspectionsLocal/liftOut/ifToAssignment/elseIfOnly.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("hasNull.kt")
|
||||
public void testHasNull() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/liftOut/ifToAssignment/hasNull.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("hasNull2.kt")
|
||||
public void testHasNull2() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/liftOut/ifToAssignment/hasNull2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("hasNull3.kt")
|
||||
public void testHasNull3() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/liftOut/ifToAssignment/hasNull3.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("hasNull4.kt")
|
||||
public void testHasNull4() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/liftOut/ifToAssignment/hasNull4.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("hasNull5.kt")
|
||||
public void testHasNull5() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/liftOut/ifToAssignment/hasNull5.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ifElseIf.kt")
|
||||
public void testIfElseIf() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/liftOut/ifToAssignment/ifElseIf.kt");
|
||||
@@ -4655,6 +4680,11 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
|
||||
runTest("idea/testData/inspectionsLocal/liftOut/ifToAssignment/simpleIfWithoutTerminatingAssignment.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("subTypes.kt")
|
||||
public void testSubTypes() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/liftOut/ifToAssignment/subTypes.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("typeMismatch.kt")
|
||||
public void testTypeMismatch() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/liftOut/ifToAssignment/typeMismatch.kt");
|
||||
|
||||
Reference in New Issue
Block a user