diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/jvm/descriptors/GetterDescriptor.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/jvm/descriptors/GetterDescriptor.java
index ab9a3623a4b0..7eb3bd89e3be 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/jvm/descriptors/GetterDescriptor.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/jvm/descriptors/GetterDescriptor.java
@@ -1,10 +1,7 @@
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.codeInspection.dataFlow.jvm.descriptors;
-import com.intellij.codeInspection.dataFlow.DfaNullability;
-import com.intellij.codeInspection.dataFlow.DfaPsiUtil;
-import com.intellij.codeInspection.dataFlow.TypeConstraint;
-import com.intellij.codeInspection.dataFlow.TypeConstraints;
+import com.intellij.codeInspection.dataFlow.*;
import com.intellij.codeInspection.dataFlow.memory.DfaMemoryState;
import com.intellij.codeInspection.dataFlow.types.DfAntiConstantType;
import com.intellij.codeInspection.dataFlow.types.DfConstantType;
@@ -91,6 +88,20 @@ public final class GetterDescriptor extends PsiVarDescriptor {
return super.createValue(factory, qualifier);
}
+ @Override
+ public @NotNull DfType restrictFromState(@NotNull DfaVariableValue qualifier, @NotNull DfaMemoryState state) {
+ CustomMethodHandlers.CustomMethodHandler handler = CustomMethodHandlers.find(myGetter);
+ if (handler != null) {
+ DfaValue value = handler.getMethodResultValue(
+ new DfaCallArguments(qualifier, DfaValue.EMPTY_ARRAY, MutationSignature.pure()),
+ state, qualifier.getFactory(), myGetter);
+ if (value != null) {
+ return state.getDfType(value);
+ }
+ }
+ return super.restrictFromState(qualifier, state);
+ }
+
@Override
public int hashCode() {
return Objects.hashCode(myGetter.getName());
diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/QualifiedValueFromConstant.java b/java/java-tests/testData/inspection/dataFlow/fixture/QualifiedValueFromConstant.java
new file mode 100644
index 000000000000..33f5008709a0
--- /dev/null
+++ b/java/java-tests/testData/inspection/dataFlow/fixture/QualifiedValueFromConstant.java
@@ -0,0 +1,19 @@
+class Scratch {
+ void test(String codeblock) {
+ if (codeblock.trim().equals("{")) {
+ if (codeblock.equals("}")) {
+ System.out.println(codeblock.trim());
+ }
+ }
+ }
+
+ enum E {A, B, C}
+
+ void test(E e) {
+ if (e.name().equals("C")) {
+ if (e == E.A) {
+ System.out.println(e.name());
+ }
+ }
+ }
+}
\ 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 defbd4f08f0e..5611433ef46c 100644
--- a/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspectionTest.java
+++ b/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspectionTest.java
@@ -760,4 +760,5 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase {
public void testInitializedViaSuperCall() { doTest(); }
public void testBoxedBooleanMethodWithCast() { doTest(); }
public void testAssignAndReturnVolatile() { doTest(); }
+ public void testQualifiedValueFromConstant() { doTest();}
}
diff --git a/platform/analysis-impl/api-dump-unreviewed.txt b/platform/analysis-impl/api-dump-unreviewed.txt
index 9327ec839c82..f9867ca43b39 100644
--- a/platform/analysis-impl/api-dump-unreviewed.txt
+++ b/platform/analysis-impl/api-dump-unreviewed.txt
@@ -2626,6 +2626,7 @@ com.intellij.codeInspection.dataFlow.value.VariableDescriptor
- isCall():Z
- isImplicitReadPossible():Z
- a:isStable():Z
+- restrictFromState(com.intellij.codeInspection.dataFlow.value.DfaVariableValue,com.intellij.codeInspection.dataFlow.memory.DfaMemoryState):com.intellij.codeInspection.dataFlow.types.DfType
c:com.intellij.codeInspection.ex.ApplicationInspectionProfileManagerBase
- com.intellij.profile.codeInspection.BaseInspectionProfileManager
- sf:Companion:com.intellij.codeInspection.ex.ApplicationInspectionProfileManagerBase$Companion
diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/dataFlow/memory/DfaMemoryStateImpl.java b/platform/analysis-impl/src/com/intellij/codeInspection/dataFlow/memory/DfaMemoryStateImpl.java
index 362ba3fa6fea..e098f5175585 100644
--- a/platform/analysis-impl/src/com/intellij/codeInspection/dataFlow/memory/DfaMemoryStateImpl.java
+++ b/platform/analysis-impl/src/com/intellij/codeInspection/dataFlow/memory/DfaMemoryStateImpl.java
@@ -19,7 +19,6 @@ import com.intellij.util.ObjectUtils;
import com.intellij.util.containers.Stack;
import it.unimi.dsi.fastutil.ints.*;
import one.util.streamex.StreamEx;
-import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -794,6 +793,14 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
return false;
}
}
+ for (Map.Entry entry : new ArrayList<>(myVariableTypes.entrySet())) {
+ DfaVariableValue knownVar = entry.getKey();
+ if (knownVar.getQualifier() == var) {
+ if (!meetDfType(knownVar, knownVar.getDescriptor().restrictFromState(var, this))) {
+ return false;
+ }
+ }
+ }
if (!updateDependentVariables(var, newType)) return false;
if (!correctRelatedValues(var, newType)) return false;
if (newType instanceof DfConstantType && !propagateConstant(var, (DfConstantType>)newType)) return false;
diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/dataFlow/value/VariableDescriptor.java b/platform/analysis-impl/src/com/intellij/codeInspection/dataFlow/value/VariableDescriptor.java
index e3b79001f70a..a70875b4cbdb 100644
--- a/platform/analysis-impl/src/com/intellij/codeInspection/dataFlow/value/VariableDescriptor.java
+++ b/platform/analysis-impl/src/com/intellij/codeInspection/dataFlow/value/VariableDescriptor.java
@@ -72,6 +72,16 @@ public interface VariableDescriptor {
@NotNull
DfType getDfType(@Nullable DfaVariableValue qualifier);
+ /**
+ * @param qualifier qualifier
+ * @param state memory state
+ * @return DfType of this value, which might be more precise than the result of {@link #getDfType(DfaVariableValue)} if the
+ * qualifier type is known to be more precise, and this information restricts the type of this value
+ */
+ default @NotNull DfType restrictFromState(@NotNull DfaVariableValue qualifier, @NotNull DfaMemoryState state) {
+ return getDfType(qualifier);
+ }
+
/**
* Returns the DfType the value with this descriptor has at the beginning of the interpretation in a given context.
*