From 9a5250258fa15a2ce50d46bd48246c04465d225b Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Thu, 7 Nov 2024 10:09:05 +0100 Subject: [PATCH] [java-dfa] GetQualifiedValueInstruction (part of IDEA-361818) GitOrigin-RevId: d8a75de6784cae450640720a89271f88c9c46cb6 --- .../dataFlow/java/CFGBuilder.java | 2 +- .../dataFlow/java/ControlFlowAnalyzer.java | 45 +++++++++++++++---- .../dataFlow/java/JavaDfaHelpers.java | 6 +++ .../dataFlow/java/JavaDfaValueFactory.java | 8 ++-- .../jvm/descriptors/PlainDescriptor.java | 13 +++--- .../jvm/descriptors/ThisDescriptor.java | 5 --- .../dataFlow/fixture/GetTernary.java | 26 +++++++++++ .../DataFlowInspectionTest.java | 1 + .../analysis-impl/api-dump-unreviewed.txt | 11 ++--- ...java => GetQualifiedValueInstruction.java} | 23 ++++++---- .../inspections/dfa/KtControlFlowBuilder.kt | 19 +++++--- .../inspections/dfa/KtControlFlowBuilder.kt | 43 ++++++++++++------ 12 files changed, 144 insertions(+), 58 deletions(-) create mode 100644 java/java-tests/testData/inspection/dataFlow/fixture/GetTernary.java rename platform/analysis-impl/src/com/intellij/codeInspection/dataFlow/lang/ir/{UnwrapDerivedVariableInstruction.java => GetQualifiedValueInstruction.java} (58%) diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/CFGBuilder.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/CFGBuilder.java index 1c88cccf17a3..bc6aa446b8a5 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/CFGBuilder.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/CFGBuilder.java @@ -132,7 +132,7 @@ public class CFGBuilder { */ @Contract("_ -> this") public @NotNull CFGBuilder unwrap(@NotNull DerivedVariableDescriptor descriptor) { - return add(new UnwrapDerivedVariableInstruction(descriptor)); + return add(new GetQualifiedValueInstruction(descriptor)); } /** diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/ControlFlowAnalyzer.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/ControlFlowAnalyzer.java index 1b465ffa2290..7d071693bda4 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/ControlFlowAnalyzer.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/ControlFlowAnalyzer.java @@ -540,7 +540,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { length = SpecialField.COLLECTION_SIZE; } if (length != null) { - addInstruction(new UnwrapDerivedVariableInstruction(length)); + addInstruction(new GetQualifiedValueInstruction(length)); addInstruction(new ConditionalGotoInstruction(loopEndOffset, DfTypes.intValue(0))); hasSizeCheck = true; } else { @@ -875,7 +875,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { } DfaControlTransferValue transferValue = exception != null ? createTransfer(exception) : null; addInstruction(new DupInstruction()); - addInstruction(new UnwrapDerivedVariableInstruction(SpecialField.CONSUMED_STREAM)); + addInstruction(new GetQualifiedValueInstruction(SpecialField.CONSUMED_STREAM)); addInstruction(new EnsureInstruction(new ConsumedStreamProblem(reference), RelationType.NE, DfStreamStateType.CONSUMED, transferValue)); addInstruction(new PopInstruction()); } @@ -1900,7 +1900,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { if (TypeConversionUtil.isPrimitiveAndNotNull(expectedType) && TypeConversionUtil.isAssignableFromPrimitiveWrapper(toBound(actualType))) { - addInstruction(new UnwrapDerivedVariableInstruction(SpecialField.UNBOX)); + addInstruction(new GetQualifiedValueInstruction(SpecialField.UNBOX)); actualType = PsiPrimitiveType.getUnboxedType(actualType); } expectedType = toBound(expectedType); @@ -2487,22 +2487,49 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { @Override public void visitReferenceExpression(@NotNull PsiReferenceExpression expression) { startElement(expression); + pushReferenceExpression(expression); + addNullCheck(expression); + finishElement(expression); + } + private void pushReferenceExpression(@NotNull PsiReferenceExpression expression) { + JavaExpressionAnchor anchor = new JavaExpressionAnchor(expression); + PsiVariable var = ObjectUtils.tryCast(expression.resolve(), PsiVariable.class); + // complex assignments (e.g. "|=") are both reading and writing + boolean writing = PsiUtil.isAccessedForWriting(expression) && !PsiUtil.isAccessedForReading(expression); + if (!writing && var != null && !PlainDescriptor.hasInitializationHacks(var)) { + DfaValue constValue = JavaDfaValueFactory.getConstantFromVariable(myFactory, var); + if (constValue != null && !JavaDfaValueFactory.maybeUninitializedConstant(constValue, expression, var)) { + addInstruction(new JvmPushInstruction(constValue, anchor)); + return; + } + } final PsiExpression qualifierExpression = expression.getQualifierExpression(); + if (var instanceof PsiField field && !field.hasModifierProperty(PsiModifier.STATIC) && !writing) { + DfaValue effectiveQualifier = JavaDfaValueFactory.getQualifierOrThisValue(myFactory, expression); + if (qualifierExpression == null) { + addInstruction(new JvmPushInstruction(effectiveQualifier == null ? myFactory.getUnknown() : effectiveQualifier, null)); + } else { + qualifierExpression.accept(this); + } + VariableDescriptor descriptor = Objects.requireNonNull(JavaDfaValueFactory.getAccessedVariableOrGetter(field)); + if (effectiveQualifier instanceof DfaVariableValue qualifierVar && + JavaDfaHelpers.mayLeakFromType(descriptor.getDfType(qualifierVar)) && + JavaDfaHelpers.mayLeakFromExpression(expression)) { + addInstruction(new EscapeInstruction(List.of(qualifierVar.getDescriptor()))); + } + addInstruction(new GetQualifiedValueInstruction(descriptor, anchor)); + return; + } if (qualifierExpression != null && !(qualifierExpression instanceof PsiReferenceExpression ref && ref.resolve() instanceof PsiClass)) { qualifierExpression.accept(this); addInstruction(new PopInstruction()); } - // complex assignments (e.g. "|=") are both reading and writing - boolean writing = PsiUtil.isAccessedForWriting(expression) && !PsiUtil.isAccessedForReading(expression); DfaValue value = JavaDfaValueFactory.getExpressionDfaValue(myFactory, expression); addInstruction(new JvmPushInstruction(value == null ? myFactory.getUnknown() : value, - writing ? null : new JavaExpressionAnchor(expression), + writing ? null : anchor, writing)); - addNullCheck(expression); - - finishElement(expression); } @Override public void visitLiteralExpression(@NotNull PsiLiteralExpression expression) { diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/JavaDfaHelpers.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/JavaDfaHelpers.java index a149c1c26dea..a10ddd8f1677 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/JavaDfaHelpers.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/JavaDfaHelpers.java @@ -13,6 +13,7 @@ import com.intellij.codeInspection.dataFlow.value.DfaVariableValue; import com.intellij.codeInspection.dataFlow.value.DfaWrappedValue; import com.intellij.psi.*; import com.intellij.psi.util.PsiUtil; +import com.siyeh.ig.psiutils.ExpressionUtils; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; @@ -73,4 +74,9 @@ public final class JavaDfaHelpers { } return (psiType == null ? DfType.TOP : TypeConstraints.exactSubtype(expression, psiType).asDfType()).meet(DfTypes.NOT_NULL_OBJECT); } + + static boolean mayLeakFromExpression(@NotNull PsiExpression expression) { + PsiElement parent = ExpressionUtils.getPassThroughParent(expression); + return !(parent instanceof PsiInstanceOfExpression) && !(parent instanceof PsiPolyadicExpression); + } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/JavaDfaValueFactory.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/JavaDfaValueFactory.java index 0a21157e50d6..378ac8a51eaf 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/JavaDfaValueFactory.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/JavaDfaValueFactory.java @@ -184,9 +184,9 @@ public final class JavaDfaValueFactory { return qualifierValue; } - private static boolean maybeUninitializedConstant(DfaValue constValue, - @NotNull PsiReferenceExpression refExpr, - PsiModifierListOwner var) { + static boolean maybeUninitializedConstant(DfaValue constValue, + @NotNull PsiReferenceExpression refExpr, + PsiModifierListOwner var) { // If static final field is referred from the same or inner/nested class, // we consider that it might be uninitialized yet as some class initializers may call its methods or // even instantiate objects of this class and call their methods @@ -252,7 +252,7 @@ public final class JavaDfaValueFactory { * @return a value that represents a constant created from variable; null if variable cannot be represented as a constant */ @Nullable - private static DfaValue getConstantFromVariable(DfaValueFactory factory, PsiVariable variable) { + static DfaValue getConstantFromVariable(DfaValueFactory factory, PsiVariable variable) { if (!variable.hasModifierProperty(PsiModifier.FINAL) || ignoreInitializer(variable)) return null; Object value = variable.computeConstantValue(); PsiType type = variable.getType(); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/jvm/descriptors/PlainDescriptor.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/jvm/descriptors/PlainDescriptor.java index c51b0ab73b8f..a0370d72b267 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/jvm/descriptors/PlainDescriptor.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/jvm/descriptors/PlainDescriptor.java @@ -10,10 +10,7 @@ import com.intellij.codeInspection.dataFlow.JavaMethodContractUtil; import com.intellij.codeInspection.dataFlow.NullabilityUtil; import com.intellij.codeInspection.dataFlow.jvm.FieldChecker; import com.intellij.codeInspection.dataFlow.types.DfTypes; -import com.intellij.codeInspection.dataFlow.value.DfaValue; -import com.intellij.codeInspection.dataFlow.value.DfaValueFactory; -import com.intellij.codeInspection.dataFlow.value.DfaVariableValue; -import com.intellij.codeInspection.dataFlow.value.VariableDescriptor; +import com.intellij.codeInspection.dataFlow.value.*; import com.intellij.psi.*; import com.intellij.psi.util.*; import com.intellij.util.JavaPsiConstructorUtil; @@ -41,7 +38,7 @@ public final class PlainDescriptor extends PsiVarDescriptor { @NotNull @Override public String toString() { - return String.valueOf(myVariable.getName()); + return PsiFormatUtil.formatVariable(myVariable, PsiFormatUtilBase.SHOW_CONTAINING_CLASS | PsiFormatUtilBase.SHOW_NAME, PsiSubstitutor.EMPTY); } @Override @@ -75,6 +72,12 @@ public final class PlainDescriptor extends PsiVarDescriptor { (myVariable instanceof PsiField && myVariable.hasModifierProperty(PsiModifier.STATIC))) { return factory.getVarFactory().createVariableValue(this); } + if (qualifier instanceof DfaTypeValue typeValue) { + PsiField field = typeValue.getDfType().getConstantOfType(PsiField.class); + if (field != null) { + qualifier = factory.getVarFactory().createVariableValue(new PlainDescriptor(field)); + } + } return super.createValue(factory, qualifier); } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/jvm/descriptors/ThisDescriptor.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/jvm/descriptors/ThisDescriptor.java index 2c2f6c352c72..4089f8eb2498 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/jvm/descriptors/ThisDescriptor.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/jvm/descriptors/ThisDescriptor.java @@ -64,11 +64,6 @@ public final class ThisDescriptor extends PsiVarDescriptor { return true; } - @Override - public boolean isImplicitReadPossible() { - return true; - } - @Override public int hashCode() { return Objects.hashCode(myQualifier.getQualifiedName()); diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/GetTernary.java b/java/java-tests/testData/inspection/dataFlow/fixture/GetTernary.java new file mode 100644 index 000000000000..26e4c4991a0a --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/GetTernary.java @@ -0,0 +1,26 @@ +import java.io.IOException; +import java.io.InputStream; + +public class GetTernary { + static class Point { + int x, y; + + Point(int x, int y) { + this.x = x; + this.y = y; + } + } + + void test(boolean b, Point p1, Point p2) { + int x = (b ? p1 : p2).x; + if (x == 1) { + if (b && p1.x == 1) {} + } + if (p1.x == 2 && p2.x == 2) { + int x1 = (b ? p1 : p2).x; + if (x1 == 2) { + + } + } + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspectionTest.java b/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspectionTest.java index 3665eb000e76..0f362ed9a8aa 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspectionTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspectionTest.java @@ -762,4 +762,5 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase { public void testFieldAliasing() { doTest();} public void testFieldLocalNoAliasing() { doTest();} public void testIoContracts() { doTest(); } + public void testGetTernary() { doTest(); } } diff --git a/platform/analysis-impl/api-dump-unreviewed.txt b/platform/analysis-impl/api-dump-unreviewed.txt index ab811f7d322a..e3698b66f914 100644 --- a/platform/analysis-impl/api-dump-unreviewed.txt +++ b/platform/analysis-impl/api-dump-unreviewed.txt @@ -1936,6 +1936,12 @@ c:com.intellij.codeInspection.dataFlow.lang.ir.FlushVariableInstruction - accept(com.intellij.codeInspection.dataFlow.interpreter.DataFlowInterpreter,com.intellij.codeInspection.dataFlow.memory.DfaMemoryState):com.intellij.codeInspection.dataFlow.lang.ir.DfaInstructionState[] - bindToFactory(com.intellij.codeInspection.dataFlow.value.DfaValueFactory):com.intellij.codeInspection.dataFlow.lang.ir.Instruction - getVariable():com.intellij.codeInspection.dataFlow.value.DfaVariableValue +c:com.intellij.codeInspection.dataFlow.lang.ir.GetQualifiedValueInstruction +- com.intellij.codeInspection.dataFlow.lang.ir.EvalInstruction +- (com.intellij.codeInspection.dataFlow.value.VariableDescriptor):V +- (com.intellij.codeInspection.dataFlow.value.VariableDescriptor,com.intellij.codeInspection.dataFlow.lang.DfaAnchor):V +- eval(com.intellij.codeInspection.dataFlow.value.DfaValueFactory,com.intellij.codeInspection.dataFlow.memory.DfaMemoryState,com.intellij.codeInspection.dataFlow.value.DfaValue[]):com.intellij.codeInspection.dataFlow.value.DfaValue +- getRequiredDescriptors(com.intellij.codeInspection.dataFlow.value.DfaValueFactory):java.util.List c:com.intellij.codeInspection.dataFlow.lang.ir.GotoInstruction - com.intellij.codeInspection.dataFlow.lang.ir.Instruction - (com.intellij.codeInspection.dataFlow.lang.ir.ControlFlow$ControlFlowOffset):V @@ -1997,11 +2003,6 @@ c:com.intellij.codeInspection.dataFlow.lang.ir.SwapInstruction - com.intellij.codeInspection.dataFlow.lang.ir.Instruction - ():V - accept(com.intellij.codeInspection.dataFlow.interpreter.DataFlowInterpreter,com.intellij.codeInspection.dataFlow.memory.DfaMemoryState):com.intellij.codeInspection.dataFlow.lang.ir.DfaInstructionState[] -c:com.intellij.codeInspection.dataFlow.lang.ir.UnwrapDerivedVariableInstruction -- com.intellij.codeInspection.dataFlow.lang.ir.EvalInstruction -- (com.intellij.codeInspection.dataFlow.value.DerivedVariableDescriptor):V -- eval(com.intellij.codeInspection.dataFlow.value.DfaValueFactory,com.intellij.codeInspection.dataFlow.memory.DfaMemoryState,com.intellij.codeInspection.dataFlow.value.DfaValue[]):com.intellij.codeInspection.dataFlow.value.DfaValue -- getRequiredDescriptors(com.intellij.codeInspection.dataFlow.value.DfaValueFactory):java.util.List c:com.intellij.codeInspection.dataFlow.lang.ir.WrapDerivedVariableInstruction - com.intellij.codeInspection.dataFlow.lang.ir.EvalInstruction - (com.intellij.codeInspection.dataFlow.types.DfType,com.intellij.codeInspection.dataFlow.value.DerivedVariableDescriptor):V diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/dataFlow/lang/ir/UnwrapDerivedVariableInstruction.java b/platform/analysis-impl/src/com/intellij/codeInspection/dataFlow/lang/ir/GetQualifiedValueInstruction.java similarity index 58% rename from platform/analysis-impl/src/com/intellij/codeInspection/dataFlow/lang/ir/UnwrapDerivedVariableInstruction.java rename to platform/analysis-impl/src/com/intellij/codeInspection/dataFlow/lang/ir/GetQualifiedValueInstruction.java index 208c9e324f62..7e6649f6515a 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/dataFlow/lang/ir/UnwrapDerivedVariableInstruction.java +++ b/platform/analysis-impl/src/com/intellij/codeInspection/dataFlow/lang/ir/GetQualifiedValueInstruction.java @@ -1,38 +1,43 @@ // Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.codeInspection.dataFlow.lang.ir; +import com.intellij.codeInspection.dataFlow.lang.DfaAnchor; import com.intellij.codeInspection.dataFlow.memory.DfaMemoryState; -import com.intellij.codeInspection.dataFlow.value.DerivedVariableDescriptor; import com.intellij.codeInspection.dataFlow.value.DfaValue; import com.intellij.codeInspection.dataFlow.value.DfaValueFactory; import com.intellij.codeInspection.dataFlow.value.VariableDescriptor; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.List; /** * Instruction to push a field qualified by the value on the stack */ -public class UnwrapDerivedVariableInstruction extends EvalInstruction { - private final @NotNull DerivedVariableDescriptor myDerivedVariableDescriptor; +public class GetQualifiedValueInstruction extends EvalInstruction { + private final @NotNull VariableDescriptor myDescriptor; - public UnwrapDerivedVariableInstruction(@NotNull DerivedVariableDescriptor derivedVariable) { - super(null, 1); - myDerivedVariableDescriptor = derivedVariable; + public GetQualifiedValueInstruction(@NotNull VariableDescriptor descriptor) { + this(descriptor, null); + } + + public GetQualifiedValueInstruction(@NotNull VariableDescriptor descriptor, @Nullable DfaAnchor anchor) { + super(anchor, 1); + myDescriptor = descriptor; } @Override public @NotNull DfaValue eval(@NotNull DfaValueFactory factory, @NotNull DfaMemoryState state, @NotNull DfaValue @NotNull ... arguments) { - return myDerivedVariableDescriptor.createValue(factory, arguments[0]); + return myDescriptor.createValue(factory, arguments[0]); } @Override public List getRequiredDescriptors(@NotNull DfaValueFactory factory) { - return List.of(myDerivedVariableDescriptor); + return List.of(myDescriptor); } @Override public String toString() { - return "UNWRAP " + myDerivedVariableDescriptor; + return "GET_FIELD " + myDescriptor; } } 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 bc9adc458865..f6b2d8abcc6b 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 @@ -349,7 +349,8 @@ class KtControlFlowBuilder(val factory: DfaValueFactory, val context: KtExpressi if (typeReference != null) { val castType = typeReference.type if (castType.toDfType() is DfPrimitiveType) { - addInstruction(UnwrapDerivedVariableInstruction(SpecialField.UNBOX)) + addInstruction( + GetQualifiedValueInstruction(SpecialField.UNBOX)) } } } @@ -515,9 +516,11 @@ class KtControlFlowBuilder(val factory: DfaValueFactory, val context: KtExpressi val leftConstraint = TypeConstraint.fromDfType(leftDfType) val rightConstraint = TypeConstraint.fromDfType(rightDfType) if (leftConstraint.isEnum && rightConstraint.isEnum && leftConstraint.meet(rightConstraint) != TypeConstraints.BOTTOM) { - addInstruction(UnwrapDerivedVariableInstruction(SpecialField.ENUM_ORDINAL)) + addInstruction( + GetQualifiedValueInstruction(SpecialField.ENUM_ORDINAL)) processExpression(right) - addInstruction(UnwrapDerivedVariableInstruction(SpecialField.ENUM_ORDINAL)) + addInstruction( + GetQualifiedValueInstruction(SpecialField.ENUM_ORDINAL)) addInstruction(BooleanBinaryInstruction(relation, forceEqualityByContent, KotlinExpressionAnchor(expr))) } else if (leftConstraint.isExact(CommonClassNames.JAVA_LANG_STRING) && rightConstraint.isExact(CommonClassNames.JAVA_LANG_STRING) @@ -854,7 +857,7 @@ class KtControlFlowBuilder(val factory: DfaValueFactory, val context: KtExpressi if (expression?.getKotlinType()?.canBeNull() == true) { addInstruction(CheckNotNullInstruction(NullabilityProblemKind.unboxingNullable.problem(expression, null)!!, trapTracker.maybeTransferValue("java.lang.NullPointerException"))) - addInstruction(UnwrapDerivedVariableInstruction(SpecialField.UNBOX)) + addInstruction(GetQualifiedValueInstruction(SpecialField.UNBOX)) } } @@ -1357,7 +1360,8 @@ class KtControlFlowBuilder(val factory: DfaValueFactory, val context: KtExpressi if (!pushJavaClassField(receiver, selector, expr)) { val specialField = (selector.mainReference?.resolveToSymbol() as? KaVariableSymbol)?.toSpecialField() if (specialField != null) { - addInstruction(UnwrapDerivedVariableInstruction(specialField)) + addInstruction( + GetQualifiedValueInstruction(specialField)) if (expr is KtSafeQualifiedExpression) { addInstruction(WrapDerivedVariableInstruction(expr.getKotlinType().toDfType(), SpecialField.UNBOX)) } @@ -1571,7 +1575,8 @@ class KtControlFlowBuilder(val factory: DfaValueFactory, val context: KtExpressi if (name == "isEmpty" || name == "isNotEmpty") { val callableId = target.callableId if (callableId != null && callableId.packageName.asString() == "kotlin.collections") { - addInstruction(UnwrapDerivedVariableInstruction(SpecialField.COLLECTION_SIZE)) + addInstruction(GetQualifiedValueInstruction( + SpecialField.COLLECTION_SIZE)) addInstruction(PushValueInstruction(DfTypes.intValue(0))) addInstruction( BooleanBinaryInstruction( @@ -1817,7 +1822,7 @@ class KtControlFlowBuilder(val factory: DfaValueFactory, val context: KtExpressi addInstruction(PushValueInstruction(actualDfType)) } if (actualDfType !is DfPrimitiveType && expectedDfType is DfPrimitiveType) { - addInstruction(UnwrapDerivedVariableInstruction(SpecialField.UNBOX)) + addInstruction(GetQualifiedValueInstruction(SpecialField.UNBOX)) } else if (expectedDfType !is DfPrimitiveType && actualDfType is DfPrimitiveType) { val dfType = actualType.withNullability(KaTypeNullability.NULLABLE).toDfType().meet(DfTypes.NOT_NULL_OBJECT) addInstruction(WrapDerivedVariableInstruction(expectedType.toDfType().meet(dfType), SpecialField.UNBOX)) diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/dfa/KtControlFlowBuilder.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/dfa/KtControlFlowBuilder.kt index 74d12a34a3e4..060da85791af 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/dfa/KtControlFlowBuilder.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/dfa/KtControlFlowBuilder.kt @@ -40,7 +40,6 @@ import org.jetbrains.kotlin.contracts.description.ContractProviderKey import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.idea.caches.resolve.analyze -import org.jetbrains.kotlin.idea.caches.resolve.resolveMainReference import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall import org.jetbrains.kotlin.idea.caches.resolve.safeAnalyzeNonSourceRootCode import org.jetbrains.kotlin.idea.codeInsight.hints.RangeKtExpressionType.* @@ -72,7 +71,6 @@ import org.jetbrains.kotlin.resolve.source.KotlinSourceElement import org.jetbrains.kotlin.resolve.source.getPsi import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.typeUtil.* -import org.jetbrains.kotlin.utils.addIfNotNull import java.util.concurrent.ConcurrentHashMap class KtControlFlowBuilder(val factory: DfaValueFactory, val context: KtExpression) { @@ -351,7 +349,9 @@ class KtControlFlowBuilder(val factory: DfaValueFactory, val context: KtExpressi if (typeReference != null) { val castType = typeReference.getAbbreviatedTypeOrType(typeReference.safeAnalyzeNonSourceRootCode(BodyResolveMode.FULL)) if (castType.toDfType() is DfPrimitiveType) { - addInstruction(UnwrapDerivedVariableInstruction(SpecialField.UNBOX)) + addInstruction( + GetQualifiedValueInstruction(SpecialField.UNBOX) + ) } } } @@ -390,7 +390,9 @@ class KtControlFlowBuilder(val factory: DfaValueFactory, val context: KtExpressi } if (curType != null && KotlinBuiltIns.isArrayOrPrimitiveArray(curType)) { if (indexType.canBeNull()) { - addInstruction(UnwrapDerivedVariableInstruction(SpecialField.UNBOX)) + addInstruction( + GetQualifiedValueInstruction(SpecialField.UNBOX) + ) } val transfer = trapTracker.maybeTransferValue("kotlin.IndexOutOfBoundsException") val elementType = expr.builtIns.getArrayElementType(curType) @@ -407,7 +409,10 @@ class KtControlFlowBuilder(val factory: DfaValueFactory, val context: KtExpressi when { KotlinBuiltIns.isString(kotlinType) -> { if (indexType.canBeNull()) { - addInstruction(UnwrapDerivedVariableInstruction(SpecialField.UNBOX)) + addInstruction( + GetQualifiedValueInstruction( + SpecialField.UNBOX) + ) } val transfer = trapTracker.maybeTransferValue("kotlin.IndexOutOfBoundsException") addInstruction(EnsureIndexInBoundsInstruction(KotlinArrayIndexProblem(SpecialField.STRING_LENGTH, idx), transfer)) @@ -419,7 +424,10 @@ class KtControlFlowBuilder(val factory: DfaValueFactory, val context: KtExpressi } isList(kotlinType) -> { if (indexType.canBeNull()) { - addInstruction(UnwrapDerivedVariableInstruction(SpecialField.UNBOX)) + addInstruction( + GetQualifiedValueInstruction( + SpecialField.UNBOX) + ) } val transfer = trapTracker.maybeTransferValue("kotlin.IndexOutOfBoundsException") addInstruction(EnsureIndexInBoundsInstruction(KotlinArrayIndexProblem(SpecialField.COLLECTION_SIZE, idx), transfer)) @@ -632,7 +640,10 @@ class KtControlFlowBuilder(val factory: DfaValueFactory, val context: KtExpressi val containingPackage = if (containingDeclaration is PackageFragmentDescriptor) containingDeclaration.fqName else (containingDeclaration as? ClassDescriptor)?.containingPackage() if (containingPackage?.asString() == "kotlin.collections") { - addInstruction(UnwrapDerivedVariableInstruction(SpecialField.COLLECTION_SIZE)) + addInstruction( + GetQualifiedValueInstruction( + SpecialField.COLLECTION_SIZE) + ) addInstruction(PushValueInstruction(DfTypes.intValue(0))) addInstruction( BooleanBinaryInstruction( @@ -816,7 +827,7 @@ class KtControlFlowBuilder(val factory: DfaValueFactory, val context: KtExpressi if (!pushJavaClassField(receiver, selector, expr)) { val specialField = findSpecialField(expr) if (specialField != null) { - addInstruction(UnwrapDerivedVariableInstruction(specialField)) + addInstruction(GetQualifiedValueInstruction(specialField)) if (expr is KtSafeQualifiedExpression) { addInstruction(WrapDerivedVariableInstruction(expr.getKotlinType().toDfType(), SpecialField.UNBOX)) } @@ -1523,7 +1534,7 @@ class KtControlFlowBuilder(val factory: DfaValueFactory, val context: KtExpressi addInstruction(PushValueInstruction(actualDfType)) } if (actualDfType !is DfPrimitiveType && expectedDfType is DfPrimitiveType) { - addInstruction(UnwrapDerivedVariableInstruction(SpecialField.UNBOX)) + addInstruction(GetQualifiedValueInstruction(SpecialField.UNBOX)) } else if (expectedDfType !is DfPrimitiveType && actualDfType is DfPrimitiveType) { val dfType = actualType.makeNullable().toDfType().meet(DfTypes.NOT_NULL_OBJECT) @@ -1562,9 +1573,13 @@ class KtControlFlowBuilder(val factory: DfaValueFactory, val context: KtExpressi val leftConstraint = TypeConstraint.fromDfType(leftDfType) val rightConstraint = TypeConstraint.fromDfType(rightDfType) if (leftConstraint.isEnum && rightConstraint.isEnum && leftConstraint.meet(rightConstraint) != TypeConstraints.BOTTOM) { - addInstruction(UnwrapDerivedVariableInstruction(SpecialField.ENUM_ORDINAL)) + addInstruction( + GetQualifiedValueInstruction(SpecialField.ENUM_ORDINAL) + ) processExpression(right) - addInstruction(UnwrapDerivedVariableInstruction(SpecialField.ENUM_ORDINAL)) + addInstruction( + GetQualifiedValueInstruction(SpecialField.ENUM_ORDINAL) + ) addInstruction(BooleanBinaryInstruction(relation, forceEqualityByContent, KotlinExpressionAnchor(expr))) } else if (leftConstraint.isExact(CommonClassNames.JAVA_LANG_STRING) && rightConstraint.isExact(CommonClassNames.JAVA_LANG_STRING)) { @@ -1685,7 +1700,9 @@ class KtControlFlowBuilder(val factory: DfaValueFactory, val context: KtExpressi addImplicitConversion(dfVarType, balancedType) addInstruction(BooleanBinaryInstruction(RelationType.EQ, true, KotlinWhenConditionAnchor(condition))) } else if (exprType?.canBeNull() == true) { - addInstruction(UnwrapDerivedVariableInstruction(SpecialField.UNBOX)) + addInstruction( + GetQualifiedValueInstruction(SpecialField.UNBOX) + ) } } is KtWhenConditionIsPattern -> { @@ -1736,7 +1753,7 @@ class KtControlFlowBuilder(val factory: DfaValueFactory, val context: KtExpressi val condition = ifExpression.condition processExpression(condition) if (condition?.getKotlinType()?.canBeNull() == true) { - addInstruction(UnwrapDerivedVariableInstruction(SpecialField.UNBOX)) + addInstruction(GetQualifiedValueInstruction(SpecialField.UNBOX)) } val skipThenOffset = DeferredOffset() val thenStatement = ifExpression.then