From b9d88ce85d8ae755d51e94c2f892bca3b30bca18 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Tue, 12 Mar 2024 09:18:28 +0100 Subject: [PATCH] [kotlin-dfa] Fixes according to review (KTIJ-27359) GitOrigin-RevId: 838a5b980591329912a99b2f6121f964b920876b --- .../dfa/KotlinClassToJavaClassInstruction.kt | 6 +- .../dfa/KotlinConstantConditionsInspection.kt | 2 +- .../dfa/KotlinDataFlowIRProvider.kt | 4 + .../dfa/KotlinFunctionCallInstruction.kt | 62 ++++---- .../codeinsight/inspections/dfa/KtClassDef.kt | 69 ++++----- .../inspections/dfa/KtControlFlowBuilder.kt | 136 ++++++++++++----- .../inspections/dfa/KtDfaHelpers.kt | 10 +- .../inspections/dfa/KtThisDescriptor.kt | 19 +-- .../inspections/dfa/KtVariableDescriptor.kt | 141 +++++++++--------- .../inspections/dfa/SmartCastHelpers.kt | 18 ++- 10 files changed, 271 insertions(+), 196 deletions(-) diff --git a/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/dfa/KotlinClassToJavaClassInstruction.kt b/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/dfa/KotlinClassToJavaClassInstruction.kt index f229bedd2126..80d806789b22 100644 --- a/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/dfa/KotlinClassToJavaClassInstruction.kt +++ b/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/dfa/KotlinClassToJavaClassInstruction.kt @@ -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) { diff --git a/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/dfa/KotlinConstantConditionsInspection.kt b/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/dfa/KotlinConstantConditionsInspection.kt index c3d8e5c85087..99cd37d9ad46 100644 --- a/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/dfa/KotlinConstantConditionsInspection.kt +++ b/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/dfa/KotlinConstantConditionsInspection.kt @@ -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 diff --git a/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/dfa/KotlinDataFlowIRProvider.kt b/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/dfa/KotlinDataFlowIRProvider.kt index 13f58441d437..ea34881182a9 100644 --- a/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/dfa/KotlinDataFlowIRProvider.kt +++ b/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/dfa/KotlinDataFlowIRProvider.kt @@ -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 } } diff --git a/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/dfa/KotlinFunctionCallInstruction.kt b/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/dfa/KotlinFunctionCallInstruction.kt index e5f157209c09..566bb1453574 100644 --- a/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/dfa/KotlinFunctionCallInstruction.kt +++ b/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/dfa/KotlinFunctionCallInstruction.kt @@ -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 { 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 } } diff --git a/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/dfa/KtClassDef.kt b/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/dfa/KtClassDef.kt index 658c36314b5c..638b66a769bc 100644 --- a/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/dfa/KtClassDef.kt +++ b/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/dfa/KtClassDef.kt @@ -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) : TypeConstraints.ClassDef { +class KtClassDef( + private val module: KtModule, + private val hash: Int, + private val cls: KtSymbolPointer, + 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 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 { diff --git a/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/dfa/KtControlFlowBuilder.kt b/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/dfa/KtControlFlowBuilder.kt index 83f8882c1f30..ec4b1dd6df47 100644 --- a/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/dfa/KtControlFlowBuilder.kt +++ b/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/dfa/KtControlFlowBuilder.kt @@ -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() - 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() } } diff --git a/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/dfa/KtDfaHelpers.kt b/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/dfa/KtDfaHelpers.kt index 6a974bda085e..5dde95111b4b 100644 --- a/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/dfa/KtDfaHelpers.kt +++ b/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/dfa/KtDfaHelpers.kt @@ -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 diff --git a/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/dfa/KtThisDescriptor.kt b/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/dfa/KtThisDescriptor.kt index 33fb917917b8..c1175d460de8 100644 --- a/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/dfa/KtThisDescriptor.kt +++ b/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/dfa/KtThisDescriptor.kt @@ -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" } } \ No newline at end of file diff --git a/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/dfa/KtVariableDescriptor.kt b/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/dfa/KtVariableDescriptor.kt index 5be290aa54dc..8461e6971172 100644 --- a/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/dfa/KtVariableDescriptor.kt +++ b/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/dfa/KtVariableDescriptor.kt @@ -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, - val type: DfType, - val hash: Int) : JvmVariableDescriptor() { +class KtVariableDescriptor( + val module: KtModule, + val pointer: KtSymbolPointer, + 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 "" 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 = @@ -98,25 +104,25 @@ class KtVariableDescriptor(val module: KtModule, val result = hashSetOf() 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 diff --git a/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/dfa/SmartCastHelpers.kt b/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/dfa/SmartCastHelpers.kt index a7742ff3d70c..7c7c464ffae8 100644 --- a/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/dfa/SmartCastHelpers.kt +++ b/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/dfa/SmartCastHelpers.kt @@ -55,18 +55,18 @@ private fun getValuesInExpression(expr: KtExpression): Map { val map = hashMapOf() 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 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 when (value) { - false -> (generateSequence(parent.nextSibling) {it.nextSibling}.filterIsInstance() + - generateSequence(parent.parent.nextSibling) {it.nextSibling}.filterIsInstance()).toList() + false -> (generateSequence(parent.nextSibling) { it.nextSibling }.filterIsInstance() + + generateSequence(parent.parent.nextSibling) { it.nextSibling }.filterIsInstance()).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 if (gParent.condition == expr && value != false) { listOfNotNull(gParent.body) } else { emptyList() } + else -> emptyList() } + else -> emptyList() } }