mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-209947 Better type constraint explanation
GitOrigin-RevId: bc97ff49908ffee155459fb81dc5e46e4fb12c59
This commit is contained in:
committed by
intellij-monorepo-bot
parent
05188a3b0b
commit
efcbb20422
+6
-8
@@ -349,23 +349,21 @@ public class TrackingRunner extends StandardDataFlowRunner {
|
||||
DfaPsiType wanted = operandValue.getFactory().createDfaType(type);
|
||||
|
||||
Pair<MemoryStateChange, TypeConstraint> fact = operandHistory.findFact(operandValue, DfaFactType.TYPE_CONSTRAINT);
|
||||
TypeConstraint constraint = fact.second == null ? TypeConstraint.empty() : fact.second;
|
||||
boolean stillSatisfied = (isInstance ? constraint.withNotInstanceofValue(wanted) : constraint.withInstanceofValue(wanted)) == null;
|
||||
while (stillSatisfied) {
|
||||
String explanation = fact.second == null ? null : fact.second.getAssignabilityExplanation(wanted, isInstance);
|
||||
while (explanation != null) {
|
||||
MemoryStateChange causeLocation = fact.first;
|
||||
if (causeLocation == null) break;
|
||||
MemoryStateChange prevHistory = causeLocation.myPrevious;
|
||||
if (prevHistory == null) break;
|
||||
fact = prevHistory.findFact(operandValue, DfaFactType.TYPE_CONSTRAINT);
|
||||
TypeConstraint prevConstraint = fact.second == null ? TypeConstraint.empty() : fact.second;
|
||||
stillSatisfied = (isInstance ? prevConstraint.withNotInstanceofValue(wanted) : prevConstraint.withInstanceofValue(wanted)) == null;
|
||||
if (!stillSatisfied) {
|
||||
CauseItem causeItem =
|
||||
new CauseItem("Type of '" + operand.getText() + "' is " + constraint.getPresentationText(operand.getType()), operand);
|
||||
String prevExplanation = prevConstraint.getAssignabilityExplanation(wanted, isInstance);
|
||||
if (prevExplanation == null) {
|
||||
CauseItem causeItem = new CauseItem(explanation, operand);
|
||||
causeItem.addChildren(new CauseItem("Type of '" + operand.getText() + "' is known from #ref", causeLocation));
|
||||
return new CauseItem[]{causeItem};
|
||||
}
|
||||
constraint = prevConstraint;
|
||||
explanation = prevExplanation;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -69,6 +69,7 @@ public abstract class TypeConstraint {
|
||||
|
||||
public abstract boolean isExact(String typeName);
|
||||
|
||||
public abstract String getAssignabilityExplanation(DfaPsiType otherType, boolean expectedAssignable);
|
||||
|
||||
static final class Exact extends TypeConstraint {
|
||||
final @NotNull DfaPsiType myType;
|
||||
@@ -95,6 +96,21 @@ public abstract class TypeConstraint {
|
||||
return type.isAssignableFrom(myType) ? null : this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAssignabilityExplanation(DfaPsiType otherType, boolean expectedAssignable) {
|
||||
boolean actual = otherType.isAssignableFrom(myType);
|
||||
if (actual != expectedAssignable) return null;
|
||||
if (expectedAssignable) {
|
||||
if (myType == otherType) {
|
||||
return "An object is already known to be " + myType;
|
||||
}
|
||||
return "An object type is exactly " + myType + " which is a subtype of " + otherType;
|
||||
}
|
||||
else {
|
||||
return "An object type is exactly " + myType + " which is not a subtype of " + otherType;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
TypeConstraint withoutType(@NotNull DfaPsiType type) {
|
||||
@@ -411,6 +427,31 @@ public abstract class TypeConstraint {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAssignabilityExplanation(DfaPsiType otherType, boolean expectedAssignable) {
|
||||
if (expectedAssignable) {
|
||||
for (DfaPsiType dfaTypeValue : myInstanceofValues) {
|
||||
if (otherType.isAssignableFrom(dfaTypeValue)) {
|
||||
return "An object is already known to be " + dfaTypeValue +
|
||||
(otherType == dfaTypeValue ? "" : " which is a subtype of " + otherType);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (DfaPsiType dfaTypeValue : myNotInstanceofValues) {
|
||||
if (dfaTypeValue.isAssignableFrom(otherType)) {
|
||||
return "An object is known to be not " + dfaTypeValue +
|
||||
(otherType == dfaTypeValue ? "" : " which is a supertype of " + otherType);
|
||||
}
|
||||
}
|
||||
for (DfaPsiType dfaTypeValue : myInstanceofValues) {
|
||||
if (!otherType.isConvertibleFrom(dfaTypeValue)) {
|
||||
return "An object is known to be " + dfaTypeValue + " which is definitely incompatible with " + otherType;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
Value is always false (s instanceof String)
|
||||
An object is known to be Number which is definitely incompatible with String (s)
|
||||
Type of 's' is known from line #10 (s instanceof Number)
|
||||
*/
|
||||
import java.util.List;
|
||||
|
||||
class Test {
|
||||
void test(Object s) {
|
||||
if (s instanceof Number) {
|
||||
if (s instanceof Integer) {
|
||||
if (<selection>s instanceof String</selection>){
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
Value is always true (s instanceof RandomAccess)
|
||||
An object is already known to be ArrayList which is a subtype of RandomAccess (s)
|
||||
Type of 's' is known from line #12 (s instanceof ArrayList)
|
||||
*/
|
||||
import java.util.*;
|
||||
|
||||
class Test {
|
||||
void test(Object s) {
|
||||
if (s instanceof Map) {
|
||||
if (s instanceof List) {
|
||||
if (s instanceof ArrayList) {
|
||||
if (s instanceof CharSequence) {
|
||||
if (<selection>s instanceof RandomAccess</selection>){
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Value is always false (s instanceof Integer)
|
||||
Type of 's' is exactly String (s)
|
||||
An object type is exactly String which is not a subtype of Integer (s)
|
||||
Type of 's' is known from line #10 (s instanceof String)
|
||||
*/
|
||||
import java.util.List;
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
Value is always true (s instanceof String)
|
||||
An object is already known to be String (s)
|
||||
Type of 's' is known from line #10 ((String)s)
|
||||
*/
|
||||
import java.util.List;
|
||||
|
||||
class Test {
|
||||
void test(Object s) {
|
||||
System.out.println(((String)s).trim());
|
||||
|
||||
|
||||
if (<selection>s instanceof String</selection>) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Value is always true (s instanceof CharSequence)
|
||||
Type of 's' is exactly String (s)
|
||||
An object type is exactly String which is a subtype of CharSequence (s)
|
||||
Type of 's' is known from line #10 (s instanceof String)
|
||||
*/
|
||||
import java.util.List;
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
Value is always false (s instanceof String)
|
||||
An object is known to be not CharSequence which is a supertype of String (s)
|
||||
Type of 's' is known from line #10 (s instanceof CharSequence)
|
||||
*/
|
||||
import java.util.List;
|
||||
|
||||
class Test {
|
||||
void test(Object s) {
|
||||
if (!(s instanceof CharSequence)) {
|
||||
if (s instanceof Integer) {
|
||||
if (<selection>s instanceof String</selection>){
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
@@ -117,4 +117,8 @@ public class DataFlowInspectionTrackerTest extends LightCodeInsightFixtureTestCa
|
||||
public void testInstanceOfNull() { doTest(); }
|
||||
public void testInstanceOfConflict() { doTest(); }
|
||||
public void testInstanceOfRedundant() { doTest(); }
|
||||
public void testInstanceOfChain() { doTest(); }
|
||||
public void testInstanceOfChain2() { doTest(); }
|
||||
public void testNotInstanceOf() { doTest(); }
|
||||
public void testInstanceOfPreviousCast() { doTest(); }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user