From 0ad41db427b24ca7ee1c533e48580cfa6efa6d8e Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Tue, 12 Mar 2024 08:43:01 +0100 Subject: [PATCH] [kotlin-dfa] Avoid capturing context in getVariablesChangedInNestedFunctions Fixes KTIJ-29069 K2: IDE K2: "Incorrect CachedValue in class KtVariableDescriptor" GitOrigin-RevId: 6b2cf38b2b519a44a4498aae2ba2ece659c22f0f --- .../inspections/dfa/KtVariableDescriptor.kt | 57 ++++++++++--------- 1 file changed, 31 insertions(+), 26 deletions(-) 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 d9730fb4ea56..5be290aa54dc 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 @@ -6,7 +6,6 @@ import com.intellij.codeInspection.dataFlow.jvm.descriptors.JvmVariableDescripto import com.intellij.codeInspection.dataFlow.types.DfType import com.intellij.codeInspection.dataFlow.value.DfaValueFactory import com.intellij.codeInspection.dataFlow.value.DfaVariableValue -import com.intellij.psi.PsiElement import com.intellij.psi.util.CachedValuesManager import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.kotlin.analysis.api.KtAnalysisSession @@ -28,16 +27,21 @@ class KtVariableDescriptor(val module: KtModule, val type: DfType, val hash: Int) : JvmVariableDescriptor() { val stable: Boolean by lazy { - analyze(module) { + 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 - return@analyze psiElement == null || !getVariablesChangedInNestedFunctions(psiElement).contains(this@KtVariableDescriptor) + val psiElement = symbol.psi?.parent as? KtElement + if (psiElement == null) return@analyze true + return@analyze psiElement } - if (symbol is KtValueParameterSymbol || symbol is KtEnumEntrySymbol || symbol is KtReceiverParameterSymbol) return@analyze true - if (symbol is KtPropertySymbol) return@analyze symbol.isVal return@analyze false + }) { + is Boolean -> result + is KtElement -> !getVariablesChangedInNestedFunctions(result).contains(this@KtVariableDescriptor) + else -> false } } @@ -89,32 +93,33 @@ class KtVariableDescriptor(val module: KtModule, this.name.hashCode()) } - context(KtAnalysisSession) - private fun getVariablesChangedInNestedFunctions(parent: PsiElement): Set = + private fun getVariablesChangedInNestedFunctions(parent: KtElement): Set = CachedValuesManager.getProjectPsiDependentCache(parent) { scope -> val result = hashSetOf() - 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(parent, 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 + 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()) } - break - } - if (parentScope != null && PsiTreeUtil.isAncestor(parent, parentScope, true)) { - result.add(target.getVariableSymbol().variableDescriptor()) } } + return@processElements true } - return@processElements true } return@getProjectPsiDependentCache result }