[kotlin-dfa] Fixes according to review (KTIJ-27359)

GitOrigin-RevId: 838a5b980591329912a99b2f6121f964b920876b
This commit is contained in:
Tagir Valeev
2024-03-12 16:55:14 +00:00
committed by intellij-monorepo-bot
parent 0ad41db427
commit b9d88ce85d
10 changed files with 271 additions and 196 deletions
@@ -10,8 +10,10 @@ import com.intellij.codeInspection.dataFlow.value.DfaValue
import com.intellij.codeInspection.dataFlow.value.DfaValueFactory
import org.jetbrains.kotlin.idea.inspections.dfa.KotlinAnchor
class KotlinClassToJavaClassInstruction(ktAnchor: KotlinAnchor.KotlinExpressionAnchor,
private val targetClassType: TypeConstraint): EvalInstruction(ktAnchor, 1) {
class KotlinClassToJavaClassInstruction(
ktAnchor: KotlinAnchor.KotlinExpressionAnchor,
private val targetClassType: TypeConstraint
) : EvalInstruction(ktAnchor, 1) {
override fun eval(factory: DfaValueFactory, state: DfaMemoryState, vararg arguments: DfaValue): DfaValue {
val arg = state.getDfType(arguments[0]).getConstantOfType(KtClassDef::class.java)
if (arg != null) {
@@ -619,7 +619,7 @@ class KotlinConstantConditionsInspection : AbstractKotlinInspection() {
// var x2 = x1 -- let's suppress this
return true
}
val typeParameterType = when(kotlinType) {
val typeParameterType = when (kotlinType) {
is KtTypeParameterType -> kotlinType
is KtIntersectionType -> kotlinType.conjuncts.find { it is KtTypeParameterType }
else -> null
@@ -29,11 +29,14 @@ class KotlinDataFlowIRProvider : DataFlowIRProvider {
parent is KtContainerNode &&
(gParent is KtIfExpression || gParent is KtWhileExpression || gParent is KtForExpression)
|| parent is KtWhenEntry -> element.textRange
parent is KtBinaryExpression && parent.right == element &&
SHORT_CIRCUITING_TOKENS.contains(parent.operationToken) ->
parent.operationReference.textRange.union(element.textRange)
parent is KtSafeQualifiedExpression && parent.selectorExpression == element ->
parent.operationTokenNode.textRange.union(element.textRange)
parent is KtBlockExpression -> {
val prevExpression = PsiTreeUtil.skipWhitespacesAndCommentsBackward(element) as? KtExpression
if (prevExpression != null && unreachableElements.contains(prevExpression)) null
@@ -47,6 +50,7 @@ class KotlinDataFlowIRProvider : DataFlowIRProvider {
}
}
}
else -> null
}
}
@@ -41,8 +41,10 @@ class KotlinFunctionCallInstruction(
override fun bindToFactory(factory: DfaValueFactory): Instruction =
if (exceptionTransfer == null) this
else KotlinFunctionCallInstruction((dfaAnchor as KotlinExpressionAnchor).expression, argCount,
qualifierOnStack, exceptionTransfer.bindToFactory(factory))
else KotlinFunctionCallInstruction(
(dfaAnchor as KotlinExpressionAnchor).expression, argCount,
qualifierOnStack, exceptionTransfer.bindToFactory(factory)
)
override fun accept(interpreter: DataFlowInterpreter, stateBefore: DfaMemoryState): Array<DfaInstructionState> {
val arguments = popArguments(stateBefore, interpreter)
@@ -84,28 +86,27 @@ class KotlinFunctionCallInstruction(
val functionSymbol = functionCall.partiallyAppliedSymbol.symbol as? KtFunctionSymbol ?: return resultValue
val callEffects = functionSymbol.contractEffects
for (effect in callEffects) {
if (effect is KtContractConditionalContractEffectDeclaration) {
val crv = effect.effect.toContractReturnValue() ?: continue
val condition = effect.condition.toCondition(factory, functionCall, arguments) ?: continue
val notCondition = condition.negate()
if (notCondition == DfaCondition.getFalse()) continue
val returnValue = crv.getDfaValue(factory, DfaCallState(stateBefore, arguments, factory.unknown))
val negated = returnValue.dfType.tryNegate() ?: continue
val negatedResult = factory.fromDfType(resultValue.dfType.meet(negated))
if (notCondition == DfaCondition.getTrue()) {
return negatedResult
}
if (negatedResult.dfType != DfType.BOTTOM) {
val notSatisfiedState = stateBefore.createCopy()
if (notSatisfiedState.applyCondition(notCondition)) {
pushResult(interpreter, notSatisfiedState, negatedResult)
result += nextState(interpreter, notSatisfiedState)
}
}
if (!stateBefore.applyCondition(condition)) {
return factory.fromDfType(DfType.BOTTOM)
if (effect !is KtContractConditionalContractEffectDeclaration) continue
val crv = effect.effect.toContractReturnValue() ?: continue
val condition = effect.condition.toCondition(factory, functionCall, arguments) ?: continue
val notCondition = condition.negate()
if (notCondition == DfaCondition.getFalse()) continue
val returnValue = crv.getDfaValue(factory, DfaCallState(stateBefore, arguments, factory.unknown))
val negated = returnValue.dfType.tryNegate() ?: continue
val negatedResult = factory.fromDfType(resultValue.dfType.meet(negated))
if (notCondition == DfaCondition.getTrue()) {
return negatedResult
}
if (negatedResult.dfType != DfType.BOTTOM) {
val notSatisfiedState = stateBefore.createCopy()
if (notSatisfiedState.applyCondition(notCondition)) {
pushResult(interpreter, notSatisfiedState, negatedResult)
result += nextState(interpreter, notSatisfiedState)
}
}
if (!stateBefore.applyCondition(condition)) {
return factory.fromDfType(DfType.BOTTOM)
}
}
return resultValue
}
@@ -116,16 +117,19 @@ class KotlinFunctionCallInstruction(
callDescriptor: KtFunctionCall<*>,
arguments: DfaCallArguments
): DfaCondition? {
return when(this) {
return when (this) {
is KtContractBooleanConstantExpression -> if (booleanConstant) DfaCondition.getTrue() else DfaCondition.getFalse()
is KtContractBooleanValueParameterExpression -> {
parameterSymbol.findDfaValue(callDescriptor, arguments)?.cond(RelationType.EQ, factory.fromDfType(DfTypes.TRUE))
}
is KtContractLogicalNotExpression -> argument.toCondition(factory, callDescriptor, arguments)?.negate()
is KtContractIsNullPredicateExpression -> argument.parameterSymbol.findDfaValue(callDescriptor, arguments)
?.cond(RelationType.equivalence(!isNegated), factory.fromDfType(DfTypes.NULL))
is KtContractIsInstancePredicateExpression -> argument.parameterSymbol.findDfaValue(callDescriptor, arguments)
?.cond(if (isNegated) RelationType.IS_NOT else RelationType.IS, factory.fromDfType(type.toDfType()))
else -> null
}
}
@@ -145,15 +149,16 @@ class KotlinFunctionCallInstruction(
}
}
private fun KtContractEffectDeclaration.toContractReturnValue():ContractReturnValue? {
private fun KtContractEffectDeclaration.toContractReturnValue(): ContractReturnValue? {
return when (this) {
is KtContractReturnsNotNullEffectDeclaration -> ContractReturnValue.returnNotNull()
is KtContractReturnsSuccessfullyEffectDeclaration -> ContractReturnValue.returnAny()
is KtContractReturnsSpecificValueEffectDeclaration -> when(value.constantType) {
is KtContractReturnsSpecificValueEffectDeclaration -> when (value.constantType) {
KtContractConstantValue.KtContractConstantType.FALSE -> ContractReturnValue.returnFalse()
KtContractConstantValue.KtContractConstantType.TRUE -> ContractReturnValue.returnTrue()
KtContractConstantValue.KtContractConstantType.NULL -> ContractReturnValue.returnNull()
}
else -> null
}
}
@@ -210,25 +215,32 @@ class KotlinFunctionCallInstruction(
"arrayOf", "booleanArrayOf", "byteArrayOf", "shortArrayOf", "charArrayOf",
"floatArrayOf", "intArrayOf", "doubleArrayOf", "longArrayOf" ->
SpecialField.ARRAY_LENGTH.asDfType(size)
"emptyList", "emptySet", "emptyMap" ->
SpecialField.COLLECTION_SIZE.asDfType(DfTypes.intValue(0))
.meet(Mutability.UNMODIFIABLE.asDfType())
"listOf" ->
SpecialField.COLLECTION_SIZE.asDfType(size)
.meet(Mutability.UNMODIFIABLE.asDfType())
"listOfNotNull", "setOfNotNull", "mapOfNotNull" ->
SpecialField.COLLECTION_SIZE.asDfType(
size.fromRelation(RelationType.LE).meet(DfTypes.intValue(0).fromRelation(RelationType.GE))
)
.meet(Mutability.UNMODIFIABLE.asDfType())
"setOf", "mapOf" ->
SpecialField.COLLECTION_SIZE.asDfType(size.toSetSize())
.meet(Mutability.UNMODIFIABLE.asDfType())
"mutableListOf", "arrayListOf" ->
SpecialField.COLLECTION_SIZE.asDfType(size)
.meet(DfTypes.LOCAL_OBJECT)
"mutableSetOf", "linkedSetOf", "hashSetOf", "hashMapOf", "linkedMapOf" ->
SpecialField.COLLECTION_SIZE.asDfType(size.toSetSize()).meet(DfTypes.LOCAL_OBJECT)
else -> null
}
}
@@ -26,7 +26,13 @@ import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtElement
import java.util.stream.Stream
class KtClassDef(val module: KtModule, val hash: Int, val cls: KtSymbolPointer<KtClassOrObjectSymbol>) : TypeConstraints.ClassDef {
class KtClassDef(
private val module: KtModule,
private val hash: Int,
private val cls: KtSymbolPointer<KtClassOrObjectSymbol>,
private val kind: KtClassKind,
private val modality: Modality?
) : TypeConstraints.ClassDef {
override fun isInheritor(superClassQualifiedName: String): Boolean =
analyze(module) {
val classLikeSymbol = cls.restoreSymbol() ?: return@analyze false
@@ -51,26 +57,13 @@ class KtClassDef(val module: KtModule, val hash: Int, val cls: KtSymbolPointer<K
return isInheritor(other) || other.isInheritor(this)
}
override fun isInterface(): Boolean = analyze(module) {
val classLikeSymbol = cls.restoreSymbol() ?: return@analyze false
classLikeSymbol.classKind == KtClassKind.INTERFACE || classLikeSymbol.classKind == KtClassKind.ANNOTATION_CLASS
}
override fun isInterface(): Boolean = kind == KtClassKind.INTERFACE || kind == KtClassKind.ANNOTATION_CLASS
override fun isEnum(): Boolean = analyze(module) {
val classLikeSymbol = cls.restoreSymbol() ?: return@analyze false
classLikeSymbol.classKind == KtClassKind.ENUM_CLASS
}
override fun isEnum(): Boolean = kind == KtClassKind.ENUM_CLASS
override fun isFinal(): Boolean = analyze(module) {
val classLikeSymbol = cls.restoreSymbol() ?: return@analyze false
classLikeSymbol.classKind != KtClassKind.ANNOTATION_CLASS &&
classLikeSymbol is KtSymbolWithModality && classLikeSymbol.modality == Modality.FINAL
}
override fun isFinal(): Boolean = kind != KtClassKind.ANNOTATION_CLASS && modality == Modality.FINAL
override fun isAbstract(): Boolean = analyze(module) {
val classLikeSymbol = cls.restoreSymbol() ?: return@analyze false
classLikeSymbol is KtSymbolWithModality && classLikeSymbol.modality == Modality.ABSTRACT
}
override fun isAbstract(): Boolean = modality == Modality.ABSTRACT
override fun getEnumConstant(ordinal: Int): PsiEnumConstant? = analyze(module) {
val classLikeSymbol = cls.restoreSymbol() ?: return@analyze null
@@ -127,7 +120,8 @@ class KtClassDef(val module: KtModule, val hash: Int, val cls: KtSymbolPointer<K
companion object {
context(KtAnalysisSession)
fun KtClassOrObjectSymbol.classDef(): KtClassDef = KtClassDef(
useSiteModule, classIdIfNonLocal?.hashCode() ?: name.hashCode(), createPointer()
useSiteModule, classIdIfNonLocal?.hashCode() ?: name.hashCode(), createPointer(),
classKind, (this as? KtSymbolWithModality)?.modality
)
fun fromJvmClassName(context: KtElement, jvmClassName: String): KtClassDef? {
@@ -142,29 +136,26 @@ class KtClassDef(val module: KtModule, val hash: Int, val cls: KtSymbolPointer<K
fun typeConstraintFactory(context: KtElement): TypeConstraints.TypeConstraintFactory {
return object : TypeConstraints.TypeConstraintFactory {
override fun create(def: TypeConstraints.ClassDef): TypeConstraint.Exact {
if (def is KtClassDef) {
return analyze(def.module) {
var symbol = def.cls.restoreSymbol() ?: return@analyze TypeConstraints.unresolved(def.qualifiedName ?: "???")
var correctedDef = def
val classId = symbol.classIdIfNonLocal
if (classId != null) {
val correctedClassId = JavaToKotlinClassMap.mapJavaToKotlin(classId.asSingleFqName())
if (correctedClassId != null) {
val correctedSymbol = getClassOrObjectSymbolByClassId(correctedClassId)
if (correctedSymbol != null) {
correctedDef = correctedSymbol.classDef()
symbol = correctedSymbol
}
}
}
return@analyze when {
symbol.classKind == KtClassKind.OBJECT -> TypeConstraints.singleton(correctedDef)
else -> TypeConstraints.exactClass(correctedDef)
override fun create(def: TypeConstraints.ClassDef): TypeConstraint.Exact = if (def !is KtClassDef) {
super.create(def)
} else analyze(def.module) {
var symbol = def.cls.restoreSymbol() ?: return@analyze TypeConstraints.unresolved(def.qualifiedName ?: "???")
var correctedDef = def
val classId = symbol.classIdIfNonLocal
if (classId != null) {
val correctedClassId = JavaToKotlinClassMap.mapJavaToKotlin(classId.asSingleFqName())
if (correctedClassId != null) {
val correctedSymbol = getClassOrObjectSymbolByClassId(correctedClassId)
if (correctedSymbol != null) {
correctedDef = correctedSymbol.classDef()
symbol = correctedSymbol
}
}
}
return super.create(def)
when {
symbol.classKind == KtClassKind.OBJECT -> TypeConstraints.singleton(correctedDef)
else -> TypeConstraints.exactClass(correctedDef)
}
}
override fun create(fqn: String): TypeConstraint.Exact {
@@ -55,6 +55,8 @@ import org.jetbrains.kotlin.idea.k2.codeinsight.inspections.dfa.KtVariableDescri
import org.jetbrains.kotlin.idea.references.KtReference
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClass
import org.jetbrains.kotlin.psi.psiUtil.parents
@@ -62,13 +64,13 @@ import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import java.util.concurrent.ConcurrentHashMap
class KtControlFlowBuilder(val factory:DfaValueFactory, val context:KtExpression) {
class KtControlFlowBuilder(val factory: DfaValueFactory, val context: KtExpression) {
private val flow = ControlFlow(factory, context)
private val constraintFactory = KtClassDef.typeConstraintFactory(context)
private val trapTracker = TrapTracker(factory, constraintFactory)
private val stringType = constraintFactory.create(StandardNames.FqNames.string.asString())
private var broken: Boolean = false
fun buildFlow(): ControlFlow? {
analyze(context) { processExpression(context) }
if (broken) return null
@@ -160,7 +162,8 @@ class KtControlFlowBuilder(val factory:DfaValueFactory, val context:KtExpression
if (classId == StandardNames.FqNames.uInt ||
classId == StandardNames.FqNames.uByte ||
classId == StandardNames.FqNames.uLong ||
classId == StandardNames.FqNames.uShort) {
classId == StandardNames.FqNames.uShort
) {
return false
}
}
@@ -199,10 +202,13 @@ class KtControlFlowBuilder(val factory:DfaValueFactory, val context:KtExpression
when (entry) {
is KtEscapeStringTemplateEntry ->
addInstruction(PushValueInstruction(DfTypes.referenceConstant(entry.unescapedValue, stringType)))
is KtLiteralStringTemplateEntry ->
addInstruction(PushValueInstruction(DfTypes.referenceConstant(entry.text, stringType)))
is KtStringTemplateEntryWithExpression ->
processExpression(entry.expression)
else ->
pushUnknown()
}
@@ -330,8 +336,7 @@ class KtControlFlowBuilder(val factory:DfaValueFactory, val context:KtExpression
val operandDfType = operandType.toDfType()
if (operandDfType is DfPrimitiveType) {
addInstruction(WrapDerivedVariableInstruction(DfTypes.NOT_NULL_OBJECT, SpecialField.UNBOX))
}
else if (operandType.isInlineClass() && !expr.getKotlinType().isInlineClass()) {
} else if (operandType.isInlineClass() && !expr.getKotlinType().isInlineClass()) {
addInstruction(PopInstruction())
addInstruction(PushValueInstruction(operandDfType))
}
@@ -373,7 +378,7 @@ class KtControlFlowBuilder(val factory:DfaValueFactory, val context:KtExpression
if (dfType is DfReferenceType) dfType.dropSpecialField() else dfType
}
return if (result is DfReferenceType)
// Convert Java to Kotlin types if necessary
// Convert Java to Kotlin types if necessary
result.convert(KtClassDef.typeConstraintFactory(typeReference))
else
result
@@ -504,7 +509,8 @@ class KtControlFlowBuilder(val factory:DfaValueFactory, val context:KtExpression
val leftDfType = leftType.toDfType()
val rightDfType = rightType.toDfType()
if ((relation == RelationType.EQ || relation == RelationType.NE) ||
(leftDfType is DfPrimitiveType && rightDfType is DfPrimitiveType)) {
(leftDfType is DfPrimitiveType && rightDfType is DfPrimitiveType)
) {
val balancedType: KtType? = balanceType(leftType, rightType, forceEqualityByContent)
val adjustedContentEquality = forceEqualityByContent && balancedType.toDfType() !is DfPrimitiveType
addImplicitConversion(left, balancedType)
@@ -525,7 +531,8 @@ class KtControlFlowBuilder(val factory:DfaValueFactory, val context:KtExpression
addInstruction(UnwrapDerivedVariableInstruction(SpecialField.ENUM_ORDINAL))
addInstruction(BooleanBinaryInstruction(relation, forceEqualityByContent, KotlinExpressionAnchor(expr)))
} else if (leftConstraint.isExact(CommonClassNames.JAVA_LANG_STRING) &&
rightConstraint.isExact(CommonClassNames.JAVA_LANG_STRING)) {
rightConstraint.isExact(CommonClassNames.JAVA_LANG_STRING)
) {
processExpression(right)
addInstruction(BooleanBinaryInstruction(relation, forceEqualityByContent, KotlinExpressionAnchor(expr)))
} else {
@@ -591,8 +598,7 @@ class KtControlFlowBuilder(val factory:DfaValueFactory, val context:KtExpression
KotlinExpressionAnchor(PsiTreeUtil.findCommonParent(expr, storedValue) as? KtExpression ?: expr)
else
KotlinExpressionAnchor(expr)
}
else null
} else null
val expectedType = if (lastIndex) expr.getKotlinType()?.toDfType() ?: DfType.TOP else DfType.TOP
val indexType = idx.getKotlinType()
if (indexType?.isInt != true) {
@@ -633,7 +639,8 @@ class KtControlFlowBuilder(val factory:DfaValueFactory, val context:KtExpression
}
addInstruction(PushValueInstruction(DfTypes.typedObject(PsiTypes.charType(), Nullability.UNKNOWN), anchor))
}
kotlinType.typeEquals("kotlin/collections/List") -> {
kotlinType.isSubTypeOf(StandardClassIds.List) -> {
if (indexType.canBeNull()) {
addInstruction(UnwrapDerivedVariableInstruction(SpecialField.UNBOX))
}
@@ -645,6 +652,7 @@ class KtControlFlowBuilder(val factory:DfaValueFactory, val context:KtExpression
}
pushUnknown()
}
else -> {
if (lastIndex && storedValue != null) {
processUnknownArrayStore(storedValue)
@@ -668,10 +676,9 @@ class KtControlFlowBuilder(val factory:DfaValueFactory, val context:KtExpression
}
context(KtAnalysisSession)
private fun KtType?.typeEquals(wantedType: String) =
this != null && (getAllSuperTypes() + this).any {
type -> type is KtNonErrorClassType && type.classId.asString() == wantedType
}
private fun KtType?.isSubTypeOf(wantedType: ClassId) =
this is KtNonErrorClassType && classId == wantedType ||
this != null && getAllSuperTypes().any { type -> type is KtNonErrorClassType && type.classId == wantedType }
context(KtAnalysisSession)
private fun processMathExpression(expr: KtBinaryExpression, mathOp: LongRangeBinOp) {
@@ -685,7 +692,8 @@ class KtControlFlowBuilder(val factory:DfaValueFactory, val context:KtExpression
addImplicitConversion(right, resultType)
}
if ((mathOp == LongRangeBinOp.DIV || mathOp == LongRangeBinOp.MOD) && resultType != null &&
(resultType.isLong || resultType.isInt)) {
(resultType.isLong || resultType.isInt)
) {
val transfer: DfaControlTransferValue? = trapTracker.maybeTransferValue("kotlin.ArithmeticException")
val zero = if (resultType.isLong) DfTypes.longValue(0) else DfTypes.intValue(0)
addInstruction(EnsureInstruction(null, RelationType.NE, zero, transfer, true))
@@ -742,8 +750,10 @@ class KtControlFlowBuilder(val factory:DfaValueFactory, val context:KtExpression
}
context(KtAnalysisSession)
private fun createTransfer(exitedStatement: PsiElement, blockToFlush: PsiElement, resultValue: DfaValue,
exitBlock: Boolean = false): InstructionTransfer {
private fun createTransfer(
exitedStatement: PsiElement, blockToFlush: PsiElement, resultValue: DfaValue,
exitBlock: Boolean = false
): InstructionTransfer {
val varsToFlush = PsiTreeUtil.findChildrenOfType(
blockToFlush,
KtProperty::class.java
@@ -792,7 +802,14 @@ class KtControlFlowBuilder(val factory:DfaValueFactory, val context:KtExpression
} else {
transfer = createTransfer(targetFunction, targetFunction, factory.unknown)
}
addInstruction(ControlTransferInstruction(factory.controlTransfer(transfer, trapTracker.getTrapsInsideElement(targetFunction))))
addInstruction(
ControlTransferInstruction(
factory.controlTransfer(
transfer,
trapTracker.getTrapsInsideElement(targetFunction)
)
)
)
return
}
}
@@ -887,6 +904,7 @@ class KtControlFlowBuilder(val factory:DfaValueFactory, val context:KtExpression
addInstruction(UnwrapDerivedVariableInstruction(SpecialField.UNBOX))
}
}
is KtWhenConditionIsPattern -> {
if (dfVar != null) {
addInstruction(JvmPushInstruction(dfVar, null))
@@ -906,6 +924,7 @@ class KtControlFlowBuilder(val factory:DfaValueFactory, val context:KtExpression
pushUnknown()
}
}
is KtWhenConditionInRange -> {
if (dfVar != null) {
addInstruction(JvmPushInstruction(dfVar, null))
@@ -914,6 +933,7 @@ class KtControlFlowBuilder(val factory:DfaValueFactory, val context:KtExpression
}
processInCheck(dfVarType, condition.rangeExpression, KotlinWhenConditionAnchor(condition), condition.isNegated)
}
else -> broken = true
}
}
@@ -950,7 +970,7 @@ class KtControlFlowBuilder(val factory:DfaValueFactory, val context:KtExpression
addInstruction(FinishElementInstruction(expr))
}
data class KotlinCatchClauseDescriptor(val clause : KtCatchClause): CatchClauseDescriptor {
data class KotlinCatchClauseDescriptor(val clause: KtCatchClause) : CatchClauseDescriptor {
override fun parameter(): VariableDescriptor? {
val parameter = clause.catchParameter ?: return null
return analyze(clause) { parameter.getParameterSymbol().variableDescriptor() }
@@ -966,7 +986,13 @@ class KtControlFlowBuilder(val factory:DfaValueFactory, val context:KtExpression
private fun processDestructuringDeclaration(expr: KtDestructuringDeclaration) {
processExpression(expr.initializer)
for (entry in expr.entries) {
addInstruction(FlushVariableInstruction(factory.varFactory.createVariableValue(entry.getDestructuringDeclarationEntrySymbol().variableDescriptor())))
addInstruction(
FlushVariableInstruction(
factory.varFactory.createVariableValue(
entry.getDestructuringDeclarationEntrySymbol().variableDescriptor()
)
)
)
}
}
@@ -1056,10 +1082,22 @@ class KtControlFlowBuilder(val factory:DfaValueFactory, val context:KtExpression
val destructuringDeclaration = parameter.destructuringDeclaration
if (destructuringDeclaration != null) {
for (entry in destructuringDeclaration.entries) {
addInstruction(FlushVariableInstruction(factory.varFactory.createVariableValue(entry.getDestructuringDeclarationEntrySymbol().variableDescriptor())))
addInstruction(
FlushVariableInstruction(
factory.varFactory.createVariableValue(
entry.getDestructuringDeclarationEntrySymbol().variableDescriptor()
)
)
)
}
} else {
addInstruction(FlushVariableInstruction(factory.varFactory.createVariableValue(parameter.getParameterSymbol().variableDescriptor())))
addInstruction(
FlushVariableInstruction(
factory.varFactory.createVariableValue(
parameter.getParameterSymbol().variableDescriptor()
)
)
)
}
}
@@ -1077,19 +1115,22 @@ class KtControlFlowBuilder(val factory:DfaValueFactory, val context:KtExpression
val dfVar = KtVariableDescriptor.createFromSimpleName(factory, receiver)
if (dfVar != null) {
val sf = when {
kotlinType.typeEquals("kotlin/collections/Collection") -> SpecialField.COLLECTION_SIZE
kotlinType.isSubTypeOf(StandardClassIds.Collection) -> SpecialField.COLLECTION_SIZE
kotlinType.isArrayOrPrimitiveArray() -> SpecialField.ARRAY_LENGTH
else -> null
}
if (sf != null) {
val size = sf.createValue(factory, dfVar)
return rangeFunction(expr, parameterVar, factory.fromDfType(DfTypes.intValue(0)),
RelationType.GE, size, RelationType.LT)
return rangeFunction(
expr, parameterVar, factory.fromDfType(DfTypes.intValue(0)),
RelationType.GE, size, RelationType.LT
)
}
}
}
}
}
is KtBinaryExpression -> {
val op = range.operationReference.getReferencedNameAsName().asString()
val (leftRelation, rightRelation) = when (op) {
@@ -1136,8 +1177,10 @@ class KtControlFlowBuilder(val factory:DfaValueFactory, val context:KtExpression
addInstruction(BooleanAndOrInstruction(false, KotlinForVisitedAnchor(expr)))
}
}
SpecialField.UNBOX, SpecialField.OPTIONAL_VALUE, SpecialField.ENUM_ORDINAL, SpecialField.CONSUMED_STREAM,
SpecialField.INSTANTIABLE_CLASS, null -> {}
SpecialField.INSTANTIABLE_CLASS, null -> {
}
}
}
addInstruction(PopInstruction())
@@ -1150,8 +1193,9 @@ class KtControlFlowBuilder(val factory:DfaValueFactory, val context:KtExpression
return when {
type.isEnum() -> SpecialField.ENUM_ORDINAL
type.isArrayOrPrimitiveArray() -> SpecialField.ARRAY_LENGTH
type.typeEquals("kotlin/collections/Collection") ||
type.typeEquals("kotlin/collections/Map") -> SpecialField.COLLECTION_SIZE
type.isSubTypeOf(StandardClassIds.Collection) ||
type.isSubTypeOf(StandardClassIds.Map) -> SpecialField.COLLECTION_SIZE
type.isString -> SpecialField.STRING_LENGTH
else -> null
}
@@ -1195,7 +1239,7 @@ class KtControlFlowBuilder(val factory:DfaValueFactory, val context:KtExpression
}
context(KtAnalysisSession)
private inline fun inlinedBlock(element: KtElement, fn : () -> Unit) {
private inline fun inlinedBlock(element: KtElement, fn: () -> Unit) {
// Transfer value is pushed to avoid emptying stack beyond this point
trapTracker.pushTrap(InsideInlinedBlockTrap(element))
addInstruction(JvmPushInstruction(factory.controlTransfer(DfaControlTransferValue.RETURN_TRANSFER, FList.emptyList()), null))
@@ -1342,7 +1386,7 @@ class KtControlFlowBuilder(val factory:DfaValueFactory, val context:KtExpression
context(KtAnalysisSession)
private fun pushJavaClassField(receiver: KtExpression, selector: KtExpression?, expr: KtQualifiedExpression): Boolean {
if (selector == null || !selector.textMatches("java")) return false
if (!receiver.getKotlinType().typeEquals("kotlin/reflect/KClass")) return false
if (!receiver.getKotlinType().isSubTypeOf(StandardClassIds.KClass)) return false
val kotlinType = expr.getKotlinType() ?: return false
val classType = TypeConstraint.fromDfType(kotlinType.toDfType())
if (!classType.isExact(CommonClassNames.JAVA_LANG_CLASS)) return false
@@ -1381,7 +1425,7 @@ class KtControlFlowBuilder(val factory:DfaValueFactory, val context:KtExpression
val functionSymbol = functionCall.partiallyAppliedSymbol.symbol as? KtFunctionSymbol ?: return EventOccurrencesRange.UNKNOWN
val callEffect = functionSymbol.contractEffects
.singleOrNull { e -> e is KtContractCallsInPlaceContractEffectDeclaration && e.valueParameterReference.parameterSymbol == parameter }
as? KtContractCallsInPlaceContractEffectDeclaration
as? KtContractCallsInPlaceContractEffectDeclaration
if (callEffect != null) {
return callEffect.occurrencesRange
}
@@ -1404,7 +1448,8 @@ class KtControlFlowBuilder(val factory:DfaValueFactory, val context:KtExpression
val offset = ControlFlow.FixedOffset(flow.instructionCount)
val endOffset = DeferredOffset()
if (kind != EventOccurrencesRange.EXACTLY_ONCE && kind != EventOccurrencesRange.MORE_THAN_ONCE &&
kind != EventOccurrencesRange.AT_LEAST_ONCE) {
kind != EventOccurrencesRange.AT_LEAST_ONCE
) {
pushUnknown()
addInstruction(ConditionalGotoInstruction(endOffset, DfTypes.TRUE))
}
@@ -1457,7 +1502,8 @@ class KtControlFlowBuilder(val factory:DfaValueFactory, val context:KtExpression
KtVariableDescriptor.getSingleLambdaParameter(factory, lambda)) ?: return false
// qualifier is on stack
val receiverType = receiver?.getKotlinType()
val argType = if (expr.parent is KtSafeQualifiedExpression) receiverType?.withNullability(KtTypeNullability.NON_NULLABLE) else receiverType
val argType =
if (expr.parent is KtSafeQualifiedExpression) receiverType?.withNullability(KtTypeNullability.NON_NULLABLE) else receiverType
addImplicitConversion(receiver, argType)
addInstruction(JvmAssignmentInstruction(null, parameter))
val functionLiteral = lambda.functionLiteral
@@ -1475,6 +1521,7 @@ class KtControlFlowBuilder(val factory:DfaValueFactory, val context:KtExpression
addInstruction(JvmPushInstruction(result, null))
addImplicitConversion(lambdaResultType, expr.getKotlinType())
}
"also", "apply" -> {
inlinedBlock(lambda) {
processExpression(bodyExpression)
@@ -1484,6 +1531,7 @@ class KtControlFlowBuilder(val factory:DfaValueFactory, val context:KtExpression
addImplicitConversion(argType, expr.getKotlinType())
addInstruction(ResultOfInstruction(KotlinExpressionAnchor(expr)))
}
"takeIf", "takeUnless" -> {
val result = flow.createTempVariable(DfTypes.BOOLEAN)
inlinedBlock(lambda) {
@@ -1561,7 +1609,7 @@ class KtControlFlowBuilder(val factory:DfaValueFactory, val context:KtExpression
var argCount = 0
var varArgCount = 0
var varArgType: DfType = DfType.BOTTOM
for ((argExpr, signature) in functionCall.argumentMapping) {
val parameterSymbol = signature.symbol
val parent = argExpr.parent
@@ -1587,7 +1635,7 @@ class KtControlFlowBuilder(val factory:DfaValueFactory, val context:KtExpression
return argCount
}
context(KtAnalysisSession)
context(KtAnalysisSession)
private fun tryPushImplicitQualifier(callInfo: KtSuccessCallInfo): Boolean {
val call = callInfo.call as? KtFunctionCall<*>
val receiver = (call?.partiallyAppliedSymbol?.dispatchReceiver as? KtImplicitReceiverValue)?.symbol
@@ -1617,6 +1665,7 @@ class KtControlFlowBuilder(val factory:DfaValueFactory, val context:KtExpression
dfType
}
}
is KtEnumEntry -> {
val ktClass = target.containingClass()
val enumConstant = ktClass?.toLightClass()?.fields?.firstOrNull { f -> f is PsiEnumConstant && f.name == target.name }
@@ -1627,6 +1676,7 @@ class KtControlFlowBuilder(val factory:DfaValueFactory, val context:KtExpression
DfType.TOP
}
}
else -> null
}
}
@@ -1650,9 +1700,11 @@ class KtControlFlowBuilder(val factory:DfaValueFactory, val context:KtExpression
return
}
}
"+" -> {
return
}
"-" -> {
addInstruction(PushValueInstruction(dfType.meetRange(LongRangeSet.point(0))))
addInstruction(SwapInstruction())
@@ -1715,9 +1767,11 @@ class KtControlFlowBuilder(val factory:DfaValueFactory, val context:KtExpression
context(KtAnalysisSession)
private fun processThisExpression(expr: KtThisExpression) {
val exprType = expr.getKotlinType()
val descriptor = ((expr.instanceReference as? KtNameReferenceExpression)?.reference as? KtReference)?.resolveToSymbol() as? KtReceiverParameterSymbol
val descriptor =
((expr.instanceReference as? KtNameReferenceExpression)?.reference as? KtReference)?.resolveToSymbol() as? KtReceiverParameterSymbol
if (descriptor != null && exprType != null) {
val function = (descriptor as? KtReceiverParameterSymbol)?.psi as? KtFunctionLiteral //(descriptor.toSourceElement as? KotlinSourceElement)?.psi as? KtFunctionLiteral
val function =
(descriptor as? KtReceiverParameterSymbol)?.psi as? KtFunctionLiteral //(descriptor.toSourceElement as? KotlinSourceElement)?.psi as? KtFunctionLiteral
val declType = descriptor.type
val varDesc = if (function != null) {
KtLambdaThisVariableDescriptor(function, declType.toDfType())
@@ -1769,8 +1823,7 @@ class KtControlFlowBuilder(val factory:DfaValueFactory, val context:KtExpression
}
if (actualDfType !is DfPrimitiveType && expectedDfType is DfPrimitiveType) {
addInstruction(UnwrapDerivedVariableInstruction(SpecialField.UNBOX))
}
else if (expectedDfType !is DfPrimitiveType && actualDfType is DfPrimitiveType) {
} else if (expectedDfType !is DfPrimitiveType && actualDfType is DfPrimitiveType) {
val dfType = actualType.withNullability(KtTypeNullability.NULLABLE).toDfType().meet(DfTypes.NOT_NULL_OBJECT)
addInstruction(WrapDerivedVariableInstruction(expectedType.toDfType().meet(dfType), SpecialField.UNBOX))
}
@@ -1817,7 +1870,8 @@ class KtControlFlowBuilder(val factory:DfaValueFactory, val context:KtExpression
companion object {
private val LOG = logger<KtControlFlowBuilder>()
private val ASSIGNMENT_TOKENS = TokenSet.create(KtTokens.EQ, KtTokens.PLUSEQ, KtTokens.MINUSEQ, KtTokens.MULTEQ, KtTokens.DIVEQ, KtTokens.PERCEQ)
private val ASSIGNMENT_TOKENS =
TokenSet.create(KtTokens.EQ, KtTokens.PLUSEQ, KtTokens.MINUSEQ, KtTokens.MULTEQ, KtTokens.DIVEQ, KtTokens.PERCEQ)
private val unsupported = ConcurrentHashMap.newKeySet<String>()
}
}
@@ -58,11 +58,13 @@ private fun KtType.toDfTypeNotNullable(): DfType {
return when (this) {
is KtNonErrorClassType -> {
// TODO: anonymous objects
when(classId) {
when (classId) {
DefaultTypeClassIds.BOOLEAN -> DfTypes.BOOLEAN
DefaultTypeClassIds.BYTE -> DfTypes.intRange(LongRangeSet.range(Byte.MIN_VALUE.toLong(), Byte.MAX_VALUE.toLong()))
DefaultTypeClassIds.CHAR -> DfTypes.intRange(
LongRangeSet.range(Character.MIN_VALUE.code.toLong(), Character.MAX_VALUE.code.toLong()))
LongRangeSet.range(Character.MIN_VALUE.code.toLong(), Character.MAX_VALUE.code.toLong())
)
DefaultTypeClassIds.SHORT -> DfTypes.intRange(LongRangeSet.range(Short.MIN_VALUE.toLong(), Short.MAX_VALUE.toLong()))
DefaultTypeClassIds.INT -> DfTypes.INT
DefaultTypeClassIds.LONG -> DfTypes.LONG
@@ -74,6 +76,7 @@ private fun KtType.toDfTypeNotNullable(): DfType {
val elementConstraint = elementDfType?.constraint ?: TypeConstraints.TOP
elementConstraint.arrayOf().asDfType().meet(DfTypes.NOT_NULL_OBJECT)
}
else -> {
val primitiveArrayElementType = StandardClassIds.elementTypeByPrimitiveArrayType[classId]
when (primitiveArrayElementType) {
@@ -99,6 +102,7 @@ private fun KtType.toDfTypeNotNullable(): DfType {
}
}
}
is KtTypeParameterType -> symbol.upperBounds.map { type -> type.toDfType() }.fold(DfType.TOP, DfType::meet)
is KtIntersectionType -> conjuncts.map { type -> type.toDfType() }.fold(DfType.TOP, DfType::meet)
else -> DfType.TOP
@@ -172,7 +176,7 @@ internal fun mathOpFromToken(ref: KtOperationReferenceExpression): LongRangeBinO
else -> null
}
internal fun mathOpFromAssignmentToken(token: IElementType): LongRangeBinOp? = when(token) {
internal fun mathOpFromAssignmentToken(token: IElementType): LongRangeBinOp? = when (token) {
KtTokens.PLUSEQ -> LongRangeBinOp.PLUS
KtTokens.MINUSEQ -> LongRangeBinOp.MINUS
KtTokens.MULTEQ -> LongRangeBinOp.MUL
@@ -7,14 +7,10 @@ import com.intellij.codeInspection.dataFlow.types.DfType
import com.intellij.codeInspection.dataFlow.value.DfaVariableValue
import com.intellij.codeInspection.dataFlow.value.VariableDescriptor
class KtThisDescriptor(private val dfType : DfType) : VariableDescriptor {
override fun isStable(): Boolean {
return true
}
class KtThisDescriptor(private val dfType: DfType) : VariableDescriptor {
override fun isStable(): Boolean = true
override fun isImplicitReadPossible(): Boolean {
return true
}
override fun isImplicitReadPossible(): Boolean = true
override fun getDfType(qualifier: DfaVariableValue?): DfType = dfType
@@ -23,9 +19,10 @@ class KtThisDescriptor(private val dfType : DfType) : VariableDescriptor {
override fun hashCode(): Int = dfType.hashCode()
override fun toString(): String {
if (dfType is DfReferenceType) {
return "${dfType.constraint}${if(dfType.nullability == DfaNullability.NULLABLE) "?" else ""}.this"
}
return "$dfType.this"
val receiver = if (dfType is DfReferenceType)
dfType.constraint.toString() + if (dfType.nullability == DfaNullability.NULLABLE) "?" else ""
else
dfType.toString()
return "$receiver.this"
}
}
@@ -22,22 +22,26 @@ import org.jetbrains.kotlin.idea.references.readWriteAccess
import org.jetbrains.kotlin.name.JvmStandardClassIds
import org.jetbrains.kotlin.psi.*
class KtVariableDescriptor(val module: KtModule,
val pointer: KtSymbolPointer<KtVariableLikeSymbol>,
val type: DfType,
val hash: Int) : JvmVariableDescriptor() {
class KtVariableDescriptor(
val module: KtModule,
val pointer: KtSymbolPointer<KtVariableLikeSymbol>,
val type: DfType,
val hash: Int
) : JvmVariableDescriptor() {
val stable: Boolean by lazy {
when(val result = analyze(module) {
val symbol = pointer.restoreSymbol() ?: return@analyze false
if (symbol is KtValueParameterSymbol || symbol is KtEnumEntrySymbol) return@analyze true
if (symbol is KtPropertySymbol) return@analyze symbol.isVal
if (symbol is KtLocalVariableSymbol) {
if (symbol.isVal) return@analyze true
val psiElement = symbol.psi?.parent as? KtElement
if (psiElement == null) return@analyze true
return@analyze psiElement
when (val result = analyze(module) {
when (val symbol = pointer.restoreSymbol()) {
is KtValueParameterSymbol, is KtEnumEntrySymbol -> return@analyze true
is KtPropertySymbol -> return@analyze symbol.isVal
is KtLocalVariableSymbol -> {
if (symbol.isVal) return@analyze true
val psiElement = symbol.psi?.parent as? KtElement
if (psiElement == null) return@analyze true
return@analyze psiElement
}
else -> return@analyze false
}
return@analyze false
}) {
is Boolean -> result
is KtElement -> !getVariablesChangedInNestedFunctions(result).contains(this@KtVariableDescriptor)
@@ -47,7 +51,7 @@ class KtVariableDescriptor(val module: KtModule,
override fun isStable(): Boolean = stable
override fun canBeCapturedInClosure(): Boolean = analyze(module) {
override fun canBeCapturedInClosure(): Boolean = analyze(module) {
val symbol = pointer.restoreSymbol() ?: return@analyze false
return@analyze symbol is KtVariableSymbol && symbol.isVal
}
@@ -62,7 +66,7 @@ class KtVariableDescriptor(val module: KtModule,
val symbol = pointer.restoreSymbol() ?: return@analyze "<unknown>"
symbol.name.asString()
}
companion object {
context(KtAnalysisSession)
fun getSingleLambdaParameter(factory: DfaValueFactory, lambda: KtLambdaExpression): DfaVariableValue? {
@@ -89,8 +93,10 @@ class KtVariableDescriptor(val module: KtModule,
context(KtAnalysisSession)
internal fun KtVariableLikeSymbol.variableDescriptor(): KtVariableDescriptor {
return KtVariableDescriptor(this.getContainingModule(), this.createPointer(), this.returnType.toDfType(),
this.name.hashCode())
return KtVariableDescriptor(
this.getContainingModule(), this.createPointer(), this.returnType.toDfType(),
this.name.hashCode()
)
}
private fun getVariablesChangedInNestedFunctions(parent: KtElement): Set<KtVariableDescriptor> =
@@ -98,25 +104,25 @@ class KtVariableDescriptor(val module: KtModule,
val result = hashSetOf<KtVariableDescriptor>()
analyze(scope) {
PsiTreeUtil.processElements(scope) { e ->
if (e is KtSimpleNameExpression && e.readWriteAccess(false).isWrite) {
val target = e.mainReference.resolve()
if (target is KtProperty && target.isLocal && PsiTreeUtil.isAncestor(scope, target, true)) {
var parentScope: KtFunction?
var context = e
while (true) {
parentScope = PsiTreeUtil.getParentOfType(context, KtFunction::class.java)
val maybeLambda = parentScope?.parent as? KtLambdaExpression
val maybeCall = (maybeLambda?.parent as? KtLambdaArgument)?.parent as? KtCallExpression
if (maybeCall != null && getInlineableLambda(maybeCall)?.lambda == maybeLambda) {
context = maybeCall
continue
}
break
}
if (parentScope != null && PsiTreeUtil.isAncestor(scope, parentScope, true)) {
result.add(target.getVariableSymbol().variableDescriptor())
}
if (e !is KtSimpleNameExpression || !e.readWriteAccess(false).isWrite) return@processElements true
val target = e.mainReference.resolve()
if (target !is KtProperty || !target.isLocal ||
!PsiTreeUtil.isAncestor(scope, target, true)
) return@processElements true
var parentScope: KtFunction?
var context = e
while (true) {
parentScope = PsiTreeUtil.getParentOfType(context, KtFunction::class.java)
val maybeLambda = parentScope?.parent as? KtLambdaExpression
val maybeCall = (maybeLambda?.parent as? KtLambdaArgument)?.parent as? KtCallExpression
if (maybeCall != null && getInlineableLambda(maybeCall)?.lambda == maybeLambda) {
context = maybeCall
continue
}
break
}
if (parentScope != null && PsiTreeUtil.isAncestor(scope, parentScope, true)) {
result.add(target.getVariableSymbol().variableDescriptor())
}
return@processElements true
}
@@ -127,49 +133,46 @@ class KtVariableDescriptor(val module: KtModule,
context(KtAnalysisSession)
fun createFromSimpleName(factory: DfaValueFactory, expr: KtExpression?): DfaVariableValue? {
val varFactory = factory.varFactory
if (expr is KtSimpleNameExpression) {
val symbol: KtSymbol? = expr.mainReference.resolveToSymbol()
if (symbol is KtVariableLikeSymbol) {
if (symbol is KtValueParameterSymbol || symbol is KtLocalVariableSymbol) {
return varFactory.createVariableValue(symbol.variableDescriptor())
}
if (isTrackableProperty(symbol)) {
val parent = expr.parent
var qualifier: DfaVariableValue? = null
if ((symbol.getContainingSymbol() as? KtClassOrObjectSymbol)?.classKind == KtClassKind.OBJECT) {
// property in an object: singleton, can track
return varFactory.createVariableValue(symbol.variableDescriptor(), null)
}
if (parent is KtQualifiedExpression && parent.selectorExpression == expr) {
val receiver = parent.receiverExpression
qualifier = createFromSimpleName(factory, receiver)
} else {
if (symbol.psi?.parent is KtFile) {
// top-level declaration
return varFactory.createVariableValue(symbol.variableDescriptor(), null)
}
val classOrObject = symbol.getContainingSymbol() as? KtClassOrObjectSymbol
if (classOrObject != null) {
val dfType = TypeConstraints.exactClass(classOrObject.classDef()).instanceOf().asDfType()
qualifier = varFactory.createVariableValue(KtThisDescriptor(dfType))
}
}
if (qualifier != null) {
return varFactory.createVariableValue(symbol.variableDescriptor(), qualifier)
}
}
if (expr !is KtSimpleNameExpression) return null
val symbol: KtVariableLikeSymbol = expr.mainReference.resolveToSymbol() as? KtVariableLikeSymbol ?: return null
if (symbol is KtValueParameterSymbol || symbol is KtLocalVariableSymbol) {
return varFactory.createVariableValue(symbol.variableDescriptor())
}
if (!isTrackableProperty(symbol)) return null
val parent = expr.parent
var qualifier: DfaVariableValue? = null
if ((symbol.getContainingSymbol() as? KtClassOrObjectSymbol)?.classKind == KtClassKind.OBJECT) {
// property in an object: singleton, can track
return varFactory.createVariableValue(symbol.variableDescriptor(), null)
}
if (parent is KtQualifiedExpression && parent.selectorExpression == expr) {
val receiver = parent.receiverExpression
qualifier = createFromSimpleName(factory, receiver)
} else {
if (symbol.psi?.parent is KtFile) {
// top-level declaration
return varFactory.createVariableValue(symbol.variableDescriptor(), null)
}
val classOrObject = symbol.getContainingSymbol() as? KtClassOrObjectSymbol
if (classOrObject != null) {
val dfType = TypeConstraints.exactClass(classOrObject.classDef()).instanceOf().asDfType()
qualifier = varFactory.createVariableValue(KtThisDescriptor(dfType))
}
}
if (qualifier != null) {
return varFactory.createVariableValue(symbol.variableDescriptor(), qualifier)
}
return null
}
private fun isTrackableProperty(target: KtVariableLikeSymbol?) =
target is KtPropertySymbol && target.getter?.isDefault != false && target.setter?.isDefault != false
target is KtPropertySymbol && target.getter?.isDefault != false && target.setter?.isDefault != false
&& !target.isDelegatedProperty && target.modality == Modality.FINAL
&& !target.isExtension && target.backingFieldSymbol?.hasAnnotation(JvmStandardClassIds.VOLATILE_ANNOTATION_CLASS_ID) == false
}
}
class KtLambdaThisVariableDescriptor(val lambda: KtFunctionLiteral, val type: DfType): JvmVariableDescriptor() {
class KtLambdaThisVariableDescriptor(val lambda: KtFunctionLiteral, val type: DfType) : JvmVariableDescriptor() {
override fun getDfType(qualifier: DfaVariableValue?): DfType = type
override fun isStable(): Boolean = true
override fun equals(other: Any?): Boolean = other is KtLambdaThisVariableDescriptor && other.lambda == lambda
@@ -55,18 +55,18 @@ private fun getValuesInExpression(expr: KtExpression): Map<KtSymbol, KtType> {
val map = hashMapOf<KtSymbol, KtType>()
SyntaxTraverser.psiTraverser(expr)
.filter(KtReferenceExpression::class.java)
.forEach { e ->
.forEach { e ->
val symbol = e.mainReference.resolveToSymbol()
if (symbol != null) {
val type = e.getKtType()
if (type != null) {
map[symbol] = type
}
}
}
return map
}
@@ -80,8 +80,10 @@ private fun getConditionScopes(expr: KtExpression, value: Boolean?): List<KtElem
} else {
emptyList()
}
is KtParenthesizedExpression ->
getConditionScopes(parent, value)
is KtBinaryExpression -> {
if (parent.operationToken != KtTokens.ANDAND && parent.operationToken != KtTokens.OROR) emptyList()
else {
@@ -90,13 +92,16 @@ private fun getConditionScopes(expr: KtExpression, value: Boolean?): List<KtElem
else getConditionScopes(parent, newValue)
}
}
is KtWhenConditionWithExpression ->
when (value) {
false -> (generateSequence(parent.nextSibling) {it.nextSibling}.filterIsInstance<KtWhenCondition>() +
generateSequence(parent.parent.nextSibling) {it.nextSibling}.filterIsInstance<KtWhenEntry>()).toList()
false -> (generateSequence(parent.nextSibling) { it.nextSibling }.filterIsInstance<KtWhenCondition>() +
generateSequence(parent.parent.nextSibling) { it.nextSibling }.filterIsInstance<KtWhenEntry>()).toList()
true -> listOfNotNull((parent.parent as? KtWhenEntry)?.expression)
else -> emptyList()
}
is KtContainerNode ->
when (val gParent = parent.parent) {
is KtIfExpression ->
@@ -117,14 +122,17 @@ private fun getConditionScopes(expr: KtExpression, value: Boolean?): List<KtElem
}
result
} else emptyList()
is KtWhileExpression ->
if (gParent.condition == expr && value != false) {
listOfNotNull(gParent.body)
} else {
emptyList()
}
else -> emptyList()
}
else -> emptyList()
}
}