IDEA-231391 Evaluate 'x.contains(x)' as 'always true'

Also, restore common known type information on state merge

GitOrigin-RevId: f40b56b17c4f1f255c4533b5c8c65bfe3d0c9ebb
This commit is contained in:
Tagir Valeev
2020-01-27 06:33:15 +00:00
committed by intellij-monorepo-bot
parent d14f04de7d
commit a0a2a7dd5d
4 changed files with 31 additions and 11 deletions
@@ -125,6 +125,9 @@ public class HardcodedContracts {
singleConditionContract(
ContractValue.argument(0).specialField(SpecialField.STRING_LENGTH), RelationType.EQ,
ContractValue.zero(), returnTrue()),
singleConditionContract(
ContractValue.argument(0), RelationType.EQ,
ContractValue.qualifier(), returnTrue()),
singleConditionContract(
ContractValue.qualifier().specialField(SpecialField.STRING_LENGTH), RelationType.LT,
ContractValue.argument(0).specialField(SpecialField.STRING_LENGTH), returnFalse())))
@@ -8,7 +8,6 @@ import com.intellij.codeInspection.dataFlow.value.DfaValue;
import com.intellij.codeInspection.dataFlow.value.DfaValueFactory;
import com.intellij.codeInspection.dataFlow.value.DfaVariableValue;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.MultiMap;
import one.util.streamex.LongStreamEx;
@@ -57,11 +56,7 @@ class StateMerger {
final Collection<DfaMemoryStateImpl> group = ContainerUtil.newArrayList(ContainerUtil.concat(group1, group2));
replacements.stripAndMerge(group, original -> {
DfaMemoryStateImpl copy = original.createCopy();
fact.removeFromState(copy);
return copy;
});
replacements.stripAndMerge(group, fact);
}
if (replacements.hasMerges()) return replacements.getMergeResult();
@@ -339,6 +334,11 @@ class StateMerger {
abstract void removeFromState(@NotNull DfaMemoryStateImpl state);
void restoreCommonState(DfaMemoryStateImpl stripped, Collection<DfaMemoryStateImpl> merged) {
DfType commonType = StreamEx.of(merged).map(s -> s.getDfType(myVar)).foldLeft(DfTypes.BOTTOM, DfType::join);
stripped.meetDfType(myVar, commonType);
}
@NotNull
static EqualityFact createEqualityFact(@NotNull DfaVariableValue var, @NotNull DfaValue val) {
if (val instanceof DfaVariableValue && val.getID() < var.getID()) {
@@ -491,22 +491,25 @@ class StateMerger {
return null;
}
private void stripAndMerge(@NotNull Collection<DfaMemoryStateImpl> group,
@NotNull Function<DfaMemoryStateImpl, DfaMemoryStateImpl> stripper) {
private void stripAndMerge(@NotNull Collection<DfaMemoryStateImpl> group, @NotNull Fact fact) {
if (group.size() <= 1) return;
MultiMap<DfaMemoryStateImpl, DfaMemoryStateImpl> strippedToOriginals = MultiMap.create();
for (DfaMemoryStateImpl original : group) {
strippedToOriginals.putValue(stripper.fun(original), original);
DfaMemoryStateImpl copy = original.createCopy();
fact.removeFromState(copy);
strippedToOriginals.putValue(copy, original);
}
for (Map.Entry<DfaMemoryStateImpl, Collection<DfaMemoryStateImpl>> entry : strippedToOriginals.entrySet()) {
Collection<DfaMemoryStateImpl> merged = entry.getValue();
if (merged.size() > 1) {
DfaMemoryStateImpl stripped = entry.getKey();
fact.restoreCommonState(stripped, merged);
for (DfaMemoryStateImpl state : merged) {
entry.getKey().afterMerge(state);
stripped.afterMerge(state);
}
myRemovedStates.addAll(merged);
myMerged.add(entry.getKey());
myMerged.add(stripped);
}
}
}
@@ -0,0 +1,13 @@
import org.jetbrains.annotations.*;
class Test {
public static boolean test(@Nullable String name, @NotNull String prefix) {
return name != null && name.startsWith(prefix) && name.length() > prefix.length();
}
void test1(String s1, String s2) {
if (<warning descr="Condition 's1.contains(s1)' is always 'true'">s1.contains(s1)</warning>) {}
if (s1.contains(s2) || <warning descr="Condition 's1.equals(s2)' is always 'false' when reached">s1.equals(s2)</warning>) {}
}
}
@@ -667,4 +667,5 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase {
public void testCompareEqualObjectWithNull() { doTest(); }
public void testNullabilityAfterCastAndInstanceOf() { doTest(); }
public void testInstanceOfTernary() { doTest(); }
public void testStringContains() { doTest(); }
}