IDEA-209947 Handle reassigned variables when fact is preserved; see through getters as variables

GitOrigin-RevId: 547cf499bdc952fc8b88af5896abffb57c628fea
This commit is contained in:
Tagir Valeev
2019-05-13 14:07:29 +03:00
committed by intellij-monorepo-bot
parent d6edd633c5
commit 17dd1432d0
4 changed files with 46 additions and 8 deletions
@@ -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<Relation> 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<T> 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;
}
}
}
@@ -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) {
@@ -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 (<selection>t == null</selection>) return;
}
System.out.println(t);
}
}
@@ -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(); }
}