mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-209947 Try to deduce relation from contracts
GitOrigin-RevId: 93c921e8b72b89658aadcc4a665cef820e25ebe7
This commit is contained in:
committed by
intellij-monorepo-bot
parent
0fdc4afedf
commit
b415bd616a
+3
-1
@@ -1342,7 +1342,9 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
|
||||
@Override
|
||||
public <T> void forceVariableFact(@NotNull DfaVariableValue var, @NotNull DfaFactType<T> factType, @Nullable T value) {
|
||||
DfaVariableState state = getVariableState(var);
|
||||
removeEquivalenceForVariableAndWrappers(var);
|
||||
if (factType.equals(DfaFactType.NULLABILITY) && value == DfaNullability.NOT_NULL && isNull(var)) {
|
||||
removeEquivalenceForVariableAndWrappers(var);
|
||||
}
|
||||
setVariableState(var, state.withFact(factType, value));
|
||||
updateEqClassesByState(var);
|
||||
}
|
||||
|
||||
+49
-20
@@ -823,26 +823,31 @@ public class TrackingRunner extends StandardDataFlowRunner {
|
||||
}
|
||||
PsiExpression expression = change.getExpression();
|
||||
if (expression != null) {
|
||||
Collection<DfaRelationValue> relations = Collections.emptyList();
|
||||
if (expression instanceof PsiBinaryExpression) {
|
||||
DfaRelationValue rel = fromBinaryExpression(change, (PsiBinaryExpression)expression);
|
||||
DfaRelationValue rel = getBinaryExpressionRelation(change, (PsiBinaryExpression)expression);
|
||||
if (rel != null) {
|
||||
if (isSameRelation(rel, value, relation)) {
|
||||
return new CauseItem(new CustomDfaProblemType("condition '" + condition + "' was checked before"), expression);
|
||||
}
|
||||
List<DfaRelationValue> chain = findDeductionChain(change, Collections.singletonList(rel), value, relation);
|
||||
if (!chain.isEmpty()) {
|
||||
CauseItem[] result = new CauseItem[0];
|
||||
for (DfaRelationValue deduced : chain) {
|
||||
CauseItem[] cause =
|
||||
findRelationCause(deduced.getRelation(), change, deduced.getLeftOperand(), change, deduced.getRightOperand());
|
||||
result = ArrayUtil.mergeArrays(result, cause);
|
||||
}
|
||||
if (result.length > 1) {
|
||||
CauseItem item = new CauseItem(new CustomDfaProblemType("condition '" + condition + "' was deduced"), (PsiElement)null);
|
||||
item.addChildren(result);
|
||||
return item;
|
||||
}
|
||||
}
|
||||
relations = Collections.singleton(rel);
|
||||
}
|
||||
}
|
||||
if (expression instanceof PsiCallExpression) {
|
||||
relations = getCallRelations((PsiCallExpression)expression);
|
||||
}
|
||||
List<DfaRelationValue> chain = findDeductionChain(change, relations, value, relation);
|
||||
if (!chain.isEmpty()) {
|
||||
CauseItem[] result = new CauseItem[0];
|
||||
for (DfaRelationValue deduced : chain) {
|
||||
CauseItem[] cause =
|
||||
findRelationCause(deduced.getRelation(), change, deduced.getLeftOperand(), change, deduced.getRightOperand());
|
||||
result = ArrayUtil.mergeArrays(result, cause);
|
||||
}
|
||||
if (result.length > 1) {
|
||||
CauseItem item = new CauseItem(new CustomDfaProblemType("condition '" + condition + "' was deduced"), (PsiElement)null);
|
||||
item.addChildren(result);
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return new CauseItem(new CustomDfaProblemType("result of '" + condition + "' is known from #ref"), expression);
|
||||
@@ -851,16 +856,19 @@ public class TrackingRunner extends StandardDataFlowRunner {
|
||||
}
|
||||
|
||||
private List<DfaRelationValue> findDeductionChain(MemoryStateChange change,
|
||||
List<DfaRelationValue> knownRelations,
|
||||
Collection<DfaRelationValue> knownRelations,
|
||||
DfaVariableValue value,
|
||||
Relation relation) {
|
||||
for (DfaRelationValue rel : knownRelations) {
|
||||
if (isSameRelation(rel, value, relation)) {
|
||||
continue;
|
||||
}
|
||||
for (Map.Entry<DfaVariableValue, TrackingDfaMemoryState.Change> entry : change.myChanges.entrySet()) {
|
||||
DfaVariableValue actualVar = entry.getKey();
|
||||
for (Relation actualRelation : entry.getValue().myAddedRelations) {
|
||||
if (isSameRelation(rel, actualVar, actualRelation)) {
|
||||
DfaValue left;
|
||||
DfaValue right = actualRelation.myCounterpart;
|
||||
DfaValue right;
|
||||
RelationType type;
|
||||
if (actualRelation.myRelationType == RelationType.EQ ||
|
||||
(relation.myRelationType != RelationType.NE && relation.myRelationType == actualRelation.myRelationType)) {
|
||||
@@ -873,11 +881,20 @@ public class TrackingRunner extends StandardDataFlowRunner {
|
||||
continue;
|
||||
}
|
||||
if (actualVar == value) {
|
||||
left = relation.myCounterpart;
|
||||
type = Objects.requireNonNull(type.getFlipped());
|
||||
left = actualRelation.myCounterpart;
|
||||
right = relation.myCounterpart;
|
||||
}
|
||||
else if (actualVar == relation.myCounterpart) {
|
||||
left = value;
|
||||
right = actualRelation.myCounterpart;
|
||||
}
|
||||
else if (actualRelation.myCounterpart == relation.myCounterpart) {
|
||||
left = value;
|
||||
right = actualVar;
|
||||
}
|
||||
else if (actualRelation.myCounterpart == value) {
|
||||
left = actualVar;
|
||||
right = relation.myCounterpart;
|
||||
}
|
||||
else {
|
||||
continue;
|
||||
@@ -910,7 +927,7 @@ public class TrackingRunner extends StandardDataFlowRunner {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private DfaRelationValue fromBinaryExpression(MemoryStateChange change, PsiBinaryExpression binOp) {
|
||||
private DfaRelationValue getBinaryExpressionRelation(MemoryStateChange change, PsiBinaryExpression binOp) {
|
||||
PsiExpression lOperand = binOp.getLOperand();
|
||||
PsiExpression rOperand = binOp.getROperand();
|
||||
MemoryStateChange leftPos = change.findExpressionPush(lOperand);
|
||||
@@ -926,6 +943,18 @@ public class TrackingRunner extends StandardDataFlowRunner {
|
||||
return null;
|
||||
}
|
||||
|
||||
private Collection<DfaRelationValue> getCallRelations(PsiCallExpression callExpression) {
|
||||
List<? extends MethodContract> contracts = JavaMethodContractUtil.getMethodCallContracts(callExpression);
|
||||
Set<DfaRelationValue> results = new LinkedHashSet<>();
|
||||
for (MethodContract contract : contracts) {
|
||||
for (ContractValue condition : contract.getConditions()) {
|
||||
DfaValue rel = condition.fromCall(getFactory(), callExpression);
|
||||
ContainerUtil.addIfNotNull(results, ObjectUtils.tryCast(rel, DfaRelationValue.class));
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
private CauseItem findNullabilityCause(MemoryStateChange factUse, DfaNullability nullability) {
|
||||
PsiExpression expression = factUse.getExpression();
|
||||
if (expression instanceof PsiTypeCastExpression) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Value is always false (b == c; line#14)
|
||||
Condition 'b != c' was deduced
|
||||
Condition 'b != a' was checked before (a == b; line#11)
|
||||
and condition 'c == a' was checked before (c == a; line#13)
|
||||
and condition 'a == c' was checked before (c == a; line#13)
|
||||
*/
|
||||
|
||||
public class T {
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
Value is always false (b.equals("x"); line#15)
|
||||
According to hard-coded contract, method 'equals' returns 'false' value when this != parameter (equals; line#15)
|
||||
Condition 'b != "x"' was deduced
|
||||
Result of 'b != a' is known from line #12 (a.equals(b); line#12)
|
||||
and result of 'a == "x"' is known from line #14 (a.equals("x"); line#14)
|
||||
*/
|
||||
|
||||
public class T {
|
||||
|
||||
void test(String a, String b) {
|
||||
if(a.equals(b)) {
|
||||
|
||||
} else if (a.equals("x")) {
|
||||
if (<selection>b.equals("x")</selection>) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-2
@@ -55,8 +55,9 @@ public class DataFlowInspectionTrackerTest extends LightCodeInsightFixtureTestCa
|
||||
assertTrue("Selected element is not an expression: " + selectedText, element instanceof PsiExpression);
|
||||
PsiExpression expression = (PsiExpression)element;
|
||||
TrackingRunner.DfaProblemType problemType = getProblemType(selectedText, expression);
|
||||
List<TrackingRunner.CauseItem> items = TrackingRunner.findProblemCause(true, false, expression, problemType);
|
||||
String dump = StreamEx.of(items).map(item -> item.dump(getEditor().getDocument())).joining("\n----\n");
|
||||
TrackingRunner.CauseItem item = TrackingRunner.findProblemCause(true, false, expression, problemType);
|
||||
assertNotNull(item);
|
||||
String dump = item.dump(getEditor().getDocument());
|
||||
PsiComment firstComment = PsiTreeUtil.findChildOfType(file, PsiComment.class);
|
||||
if (firstComment == null) {
|
||||
fail("Comment not found");
|
||||
@@ -175,4 +176,5 @@ public class DataFlowInspectionTrackerTest extends LightCodeInsightFixtureTestCa
|
||||
public void testConstructorSimple() { doTest(); }
|
||||
public void testConstructorDependOnInitializer() { doTest(); }
|
||||
public void testBasedOnPreviousRelation() { doTest(); }
|
||||
public void testBasedOnPreviousRelationContracts() { doTest(); }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user