diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java index 23cb372d132b..95e5267c2549 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java @@ -675,12 +675,32 @@ public class DfaMemoryStateImpl implements DfaMemoryState { return false; } setVariableState((DfaVariableValue)value, newState); + if (newState.getTypeConstraint().isExact(CommonClassNames.JAVA_LANG_STRING) && + !newState.getTypeConstraint().equals(oldState.getTypeConstraint())) { + // Type is narrowed to java.lang.String: we consider String equivalence by content, + // but other object types by reference, so we need to remove distinct pairs, if any. + convertReferenceEqualityToValueEquality(value); + } updateEquivalentVariables((DfaVariableValue)value, newState); return updateEqClassesByState((DfaVariableValue)value); } return true; } + private void convertReferenceEqualityToValueEquality(DfaValue value) { + int id = value.getID(); + int[] indices = myIdToEqClassesIndices.get(id); + for (int index : indices) { + for (Iterator iterator = myDistinctClasses.iterator(); iterator.hasNext(); ) { + DistinctPairSet.DistinctPair pair = iterator.next(); + EqClass otherClass = pair.getOtherClass(index); + if (otherClass != null && otherClass.findConstant(false) != getFactory().getConstFactory().getNull()) { + iterator.remove(); + } + } + } + } + private boolean updateEqClassesByState(DfaVariableValue value) { if (DfaNullability.isNotNull(getVariableState(value).myFactMap)) { return applyRelation(value, getFactory().getConstFactory().getNull(), true); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/TypeConstraint.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/TypeConstraint.java index b7f3471f3580..986651df6d3f 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/TypeConstraint.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/TypeConstraint.java @@ -68,6 +68,8 @@ public abstract class TypeConstraint { public abstract boolean isExact(); + public abstract boolean isExact(String typeName); + static final class Exact extends TypeConstraint { final @NotNull DfaPsiType myType; @@ -159,6 +161,11 @@ public abstract class TypeConstraint { return true; } + @Override + public boolean isExact(String typeName) { + return myType.getPsiType().equalsToText(typeName); + } + @Override public int hashCode() { return myType.hashCode(); @@ -404,6 +411,11 @@ public abstract class TypeConstraint { return false; } + @Override + public boolean isExact(String typeName) { + return false; + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/StringEquality.java b/java/java-tests/testData/inspection/dataFlow/fixture/StringEquality.java index 03af595d074f..09de2cc8d23b 100644 --- a/java/java-tests/testData/inspection/dataFlow/fixture/StringEquality.java +++ b/java/java-tests/testData/inspection/dataFlow/fixture/StringEquality.java @@ -42,10 +42,14 @@ class StringEquality { } boolean compare(Object a, Object b) { - if(a == b) return true; + if(a == b) { + if(a instanceof String) { + return ((String)a).equals(b); + } + return true; + } if(a instanceof String && b instanceof String) { - // false-positive - return ((String)a).equals((String)b); + return ((String)a).equals(b); } return false; }