diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/TrackingDfaMemoryState.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/TrackingDfaMemoryState.java index 8cd4808c5013..d402535e543c 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/TrackingDfaMemoryState.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/TrackingDfaMemoryState.java @@ -1,6 +1,7 @@ // Copyright 2000-2019 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; +import com.intellij.codeInspection.dataFlow.instructions.AssignInstruction; import com.intellij.codeInspection.dataFlow.instructions.ConditionalGotoInstruction; import com.intellij.codeInspection.dataFlow.instructions.ExpressionPushingInstruction; import com.intellij.codeInspection.dataFlow.instructions.Instruction; @@ -331,6 +332,7 @@ public class TrackingDfaMemoryState extends DfaMemoryStateImpl { MemoryStateChange findRelation(DfaVariableValue value, @NotNull Predicate relationPredicate, boolean startFromSelf) { return findChange(change -> { + if (change.myInstruction instanceof AssignInstruction && change.myTopOfStack == value) return true; Change varChange = change.myChanges.get(value); if (varChange != null && varChange.myAddedRelations.stream().anyMatch(relationPredicate)) return true; Change bridgeVarChange = change.myBridgeChanges.get(value); @@ -346,6 +348,10 @@ public class TrackingDfaMemoryState extends DfaMemoryStateImpl { if (factPair != null) return factPair; factPair = factFromChange(type, change, change.myBridgeChanges.get(value)); if (factPair != null) return factPair; + if (change.myInstruction instanceof AssignInstruction && change.myTopOfStack == value && change.myPrevious != null) { + FactDefinition fact = change.myPrevious.findFact(value, type); + return new FactDefinition<>(change, fact.myFact); + } } return new FactDefinition<>(null, ((DfaVariableValue)value).getInherentFacts().get(type)); } @@ -508,5 +514,10 @@ public class TrackingDfaMemoryState extends DfaMemoryStateImpl { T getFact(T defaultFact) { return myFact == null ? defaultFact : myFact; } + + @Override + public String toString() { + return myFact + " @ " + myChange; + } } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/TrackingRunner.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/TrackingRunner.java index 25ca9c457826..a2e69cb2678b 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/TrackingRunner.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/TrackingRunner.java @@ -800,14 +800,20 @@ public class TrackingRunner extends StandardDataFlowRunner { PsiMethodCallExpression call = (PsiMethodCallExpression)expression; PsiMethod method = call.resolveMethod(); CauseItem causeItem = fromMemberNullability(nullability, method, "method", call.getMethodExpression().getReferenceNameElement()); - if (causeItem != null) return causeItem; - switch (nullability) { - case NULL: - case NULLABLE: - return fromCallContract(factUse, call, ContractReturnValue.returnNull()); - case NOT_NULL: - return fromCallContract(factUse, call, ContractReturnValue.returnNotNull()); - default: + if (causeItem == null) { + switch (nullability) { + case NULL: + case NULLABLE: + causeItem = fromCallContract(factUse, call, ContractReturnValue.returnNull()); + break; + case NOT_NULL: + causeItem = fromCallContract(factUse, call, ContractReturnValue.returnNotNull()); + break; + default: + } + } + if (causeItem != null) { + return causeItem; } } if (expression instanceof PsiReferenceExpression) { diff --git a/java/java-tests/testData/inspection/dataFlow/tracker/ReassignAfterCheck.java b/java/java-tests/testData/inspection/dataFlow/tracker/ReassignAfterCheck.java new file mode 100644 index 000000000000..2a8891fac766 --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/tracker/ReassignAfterCheck.java @@ -0,0 +1,20 @@ +/* +Value is always false (t == null; line#16) + 't' was assigned (=; line#15) + The 'instanceof' check implies non-nullity (t.getNext() instanceof CharSequence; line#14) + */ + +import org.jetbrains.annotations.Contract; + +class Test { + @Contract(pure=true) + native Test getNext(); + + void find(Test t) { + while (t.getNext() instanceof CharSequence) { + t = t.getNext(); + if (t == null) return; + } + System.out.println(t); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspectionTrackerTest.java b/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspectionTrackerTest.java index b8a2ef574abc..f0fa26cd80a2 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspectionTrackerTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspectionTrackerTest.java @@ -170,4 +170,5 @@ public class DataFlowInspectionTrackerTest extends LightCodeInsightFixtureTestCa public void testCollectionSizeEquality() { doTest(); } public void testFailingCall() { doTest(); } public void testInstanceOfMethodReturn() { doTest(); } + public void testReassignAfterCheck() { doTest(); } }