From d50f41f7aefdd88e1dcf904ddc5329bab5211a41 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Tue, 11 Jun 2024 15:13:52 +0200 Subject: [PATCH] [java-dfa] Restrict the result of CustomMethodHandler when qualifier is restricted Fixes EA-1080132 - ISE: MethodCallInstruction.getMethodResultValue (String.trim, Enum.name) GitOrigin-RevId: 3ab2fe4fac1d9333d0a61198d2614eead74a173c --- .../jvm/descriptors/GetterDescriptor.java | 19 +++++++++++++++---- .../fixture/QualifiedValueFromConstant.java | 19 +++++++++++++++++++ .../DataFlowInspectionTest.java | 1 + .../analysis-impl/api-dump-unreviewed.txt | 1 + .../dataFlow/memory/DfaMemoryStateImpl.java | 9 ++++++++- .../dataFlow/value/VariableDescriptor.java | 10 ++++++++++ 6 files changed, 54 insertions(+), 5 deletions(-) create mode 100644 java/java-tests/testData/inspection/dataFlow/fixture/QualifiedValueFromConstant.java 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. *