[kotlin-dfa] Limited boxing support

GitOrigin-RevId: 5a6824c2921e4ebf4c68437b5e7f3c31e88cc692
This commit is contained in:
Tagir Valeev
2021-05-24 12:56:46 +07:00
committed by intellij-monorepo-bot
parent 112174f6b2
commit 0111a86d02
7 changed files with 65 additions and 6 deletions

View File

@@ -2,6 +2,7 @@
package org.jetbrains.kotlin.idea.inspections.dfa
import com.intellij.codeInspection.dataFlow.java.inst.*
import com.intellij.codeInspection.dataFlow.jvm.SpecialField
import com.intellij.codeInspection.dataFlow.lang.ir.*
import com.intellij.codeInspection.dataFlow.lang.ir.ControlFlow.DeferredOffset
import com.intellij.codeInspection.dataFlow.rangeSet.LongRangeBinOp
@@ -96,7 +97,7 @@ class KtControlFlowBuilder(val factory: DfaValueFactory, val context: KtExpressi
processExpression(operand)
val anchor = KotlinExpressionAnchor(expr)
if (operand != null) {
val dfType = operand.getKotlinType().toDfType()
val dfType = operand.getKotlinType().toDfType(expr)
val descriptor = KtLocalVariableDescriptor.create(operand)
val ref = expr.operationReference.text
if (dfType is DfIntegralType) {
@@ -197,6 +198,9 @@ class KtControlFlowBuilder(val factory: DfaValueFactory, val context: KtExpressi
val descriptor = KtLocalVariableDescriptor.create(expr)
if (descriptor != null) {
addInstruction(JvmPushInstruction(descriptor.createValue(factory, null), KotlinExpressionAnchor(expr)))
val exprType = expr.getKotlinType()
val declaredType = descriptor.variable.type()
addImplicitConversion(expr, declaredType, exprType)
return
}
broken = true
@@ -217,7 +221,7 @@ class KtControlFlowBuilder(val factory: DfaValueFactory, val context: KtExpressi
processBinaryRelationExpression(expr, relation, token == KtTokens.EXCLEQ || token == KtTokens.EQEQ)
return
}
val leftType = expr.left?.getKotlinType()?.toDfType() ?: DfType.TOP
val leftType = expr.left?.getKotlinType()?.toDfType(expr) ?: DfType.TOP
if (leftType is DfIntegralType) {
val mathOp = mathOpFromToken(expr.operationReference)
if (mathOp != null) {
@@ -332,6 +336,13 @@ class KtControlFlowBuilder(val factory: DfaValueFactory, val context: KtExpressi
if (actualType == expectedType) return
val actualPsiType = actualType.toPsiType(expression)
val expectedPsiType = expectedType.toPsiType(expression)
if (actualType.isMarkedNullable && !expectedType.isMarkedNullable && expectedPsiType is PsiPrimitiveType) {
addInstruction(UnwrapDerivedVariableInstruction(SpecialField.UNBOX))
}
else if (!actualType.isMarkedNullable && expectedType.isMarkedNullable && actualPsiType is PsiPrimitiveType) {
addInstruction(WrapDerivedVariableInstruction(
expectedType.toDfType(expression).meet(DfTypes.NOT_NULL_OBJECT), SpecialField.UNBOX))
}
if (actualPsiType is PsiPrimitiveType && expectedPsiType is PsiPrimitiveType) {
addInstruction(PrimitiveConversionInstruction(expectedPsiType, null))
}

View File

@@ -1,8 +1,13 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.kotlin.idea.inspections.dfa
import com.intellij.codeInsight.Nullability
import com.intellij.codeInspection.dataFlow.DfaNullability
import com.intellij.codeInspection.dataFlow.jvm.SpecialField
import com.intellij.codeInspection.dataFlow.rangeSet.LongRangeBinOp
import com.intellij.codeInspection.dataFlow.rangeSet.LongRangeSet
import com.intellij.codeInspection.dataFlow.types.DfPrimitiveType
import com.intellij.codeInspection.dataFlow.types.DfReferenceType
import com.intellij.codeInspection.dataFlow.types.DfType
import com.intellij.codeInspection.dataFlow.types.DfTypes
import com.intellij.codeInspection.dataFlow.value.RelationType
@@ -22,9 +27,21 @@ import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.*
internal fun KotlinType?.toDfType() : DfType {
internal fun KotlinType?.toDfType(context: PsiElement) : DfType {
if (this == null) return DfType.TOP
if (isMarkedNullable) {
var notNullableType = makeNotNullable().toDfType(context)
if (notNullableType is DfPrimitiveType) {
notNullableType = SpecialField.UNBOX.asDfType(notNullableType)
.meet(DfTypes.typedObject(toPsiType(context), Nullability.UNKNOWN))
}
return if (notNullableType is DfReferenceType) {
notNullableType.dropNullability().meet(DfaNullability.NULLABLE.asDfType())
} else {
notNullableType
}
}
return when {
this == null -> DfType.TOP
isBoolean() -> DfTypes.BOOLEAN
isByte() -> DfTypes.intRange(LongRangeSet.range(Byte.MIN_VALUE.toLong(), Byte.MAX_VALUE.toLong()))
isChar() -> DfTypes.intRange(LongRangeSet.range(Character.MIN_VALUE.toLong(), Character.MAX_VALUE.toLong()))
@@ -33,7 +50,7 @@ internal fun KotlinType?.toDfType() : DfType {
isLong() -> DfTypes.LONG
isFloat() -> DfTypes.FLOAT
isDouble() -> DfTypes.DOUBLE
else -> DfType.TOP
else -> DfTypes.typedObject(toPsiType(context), Nullability.NOT_NULL)
}
}

View File

@@ -13,7 +13,7 @@ import org.jetbrains.kotlin.psi.*
class KtLocalVariableDescriptor(val variable : KtCallableDeclaration) : VariableDescriptor {
override fun isStable(): Boolean = true
override fun getDfType(qualifier: DfaVariableValue?): DfType = variable.type().toDfType()
override fun getDfType(qualifier: DfaVariableValue?): DfType = variable.type().toDfType(variable)
override fun createValue(factory: DfaValueFactory, qualifier: DfaValue?): DfaValue {
assert(qualifier == null) { "Local variable descriptor should not be qualified, got qualifier '$qualifier'" }

View File

@@ -4466,6 +4466,21 @@ public abstract class LocalInspectionTestGenerated extends AbstractLocalInspecti
runTest("testData/inspectionsLocal/dfa/booleanNot.kt");
}
@TestMetadata("boxing.kt")
public void testBoxing() throws Exception {
runTest("testData/inspectionsLocal/dfa/boxing.kt");
}
@TestMetadata("boxing2.kt")
public void testBoxing2() throws Exception {
runTest("testData/inspectionsLocal/dfa/boxing2.kt");
}
@TestMetadata("boxing3.kt")
public void testBoxing3() throws Exception {
runTest("testData/inspectionsLocal/dfa/boxing3.kt");
}
@TestMetadata("declaration.kt")
public void testDeclaration() throws Exception {
runTest("testData/inspectionsLocal/dfa/declaration.kt");

View File

@@ -0,0 +1,4 @@
// PROBLEM: none
fun test(x : Int?) {
if (x != null && <caret>x > 5) {}
}

View File

@@ -0,0 +1,5 @@
// PROBLEM: Condition is always false
// FIX: none
fun test(x : Int?) {
if (x != null && x > 5 && <caret>x < 3) {}
}

View File

@@ -0,0 +1,7 @@
// PROBLEM: Condition is always false
// FIX: none
fun test(y : Int) {
var x : Int?
x = y
if (<caret>x == null) {}
}