mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Map contracts; more Collection contracts; minor API changes
This commit is contained in:
@@ -41,6 +41,10 @@ public abstract class ContractValue {
|
||||
return new Argument(index);
|
||||
}
|
||||
|
||||
public ContractValue specialField(SpecialField field) {
|
||||
return new Spec(this, field);
|
||||
}
|
||||
|
||||
public static ContractValue constant(Object value, PsiType type) {
|
||||
return new IndependentValue(factory -> factory.getConstFactory().createFromValue(value, type, null), String.valueOf(value));
|
||||
}
|
||||
@@ -57,8 +61,8 @@ public abstract class ContractValue {
|
||||
return IndependentValue.NULL;
|
||||
}
|
||||
|
||||
public static ContractValue specialField(ContractValue qualifier, SpecialField field) {
|
||||
return new Spec(qualifier, field);
|
||||
public static ContractValue zero() {
|
||||
return IndependentValue.ZERO;
|
||||
}
|
||||
|
||||
public static ContractValue condition(ContractValue left, DfaRelationValue.RelationType relation, ContractValue right) {
|
||||
@@ -103,6 +107,8 @@ public abstract class ContractValue {
|
||||
static final IndependentValue FALSE = new IndependentValue(factory -> factory.getConstFactory().getFalse(), "false");
|
||||
static final IndependentValue PRESENT = new IndependentValue(factory -> factory.getOptionalFactory().getOptional(true), "present");
|
||||
static final IndependentValue ABSENT = new IndependentValue(factory -> factory.getOptionalFactory().getOptional(false), "empty");
|
||||
static final IndependentValue ZERO =
|
||||
new IndependentValue(factory -> factory.getConstFactory().createFromValue(0, PsiType.INT, null), "0");
|
||||
|
||||
private final Function<DfaValueFactory, DfaValue> mySupplier;
|
||||
private final String myPresentation;
|
||||
|
||||
+8
-5
@@ -41,7 +41,9 @@ public class CustomMethodHandlers {
|
||||
|
||||
private static final CallMapper<CustomMethodHandler> CUSTOM_METHOD_HANDLERS = new CallMapper<CustomMethodHandler>()
|
||||
.register(instanceCall(JAVA_LANG_STRING, "indexOf", "lastIndexOf"),
|
||||
(args, memState, factory) -> stringIndexOf(args.myQualifier, memState, factory))
|
||||
(args, memState, factory) -> indexOf(args.myQualifier, memState, factory, SpecialField.STRING_LENGTH))
|
||||
.register(instanceCall(JAVA_UTIL_LIST, "indexOf", "lastIndexOf"),
|
||||
(args, memState, factory) -> indexOf(args.myQualifier, memState, factory, SpecialField.COLLECTION_SIZE))
|
||||
.register(instanceCall(JAVA_LANG_STRING, "equals").parameterCount(1),
|
||||
(args, memState, factory) -> stringEquals(args, memState, factory, false))
|
||||
.register(instanceCall(JAVA_LANG_STRING, "equalsIgnoreCase").parameterCount(1),
|
||||
@@ -105,10 +107,11 @@ public class CustomMethodHandlers {
|
||||
return applyCondition(memState, trueRelation, DfaUnknownValue.getInstance(), falseRelation, factory.getBoolean(false));
|
||||
}
|
||||
|
||||
private static List<DfaMemoryState> stringIndexOf(DfaValue qualifier,
|
||||
DfaMemoryState memState,
|
||||
DfaValueFactory factory) {
|
||||
DfaValue length = SpecialField.STRING_LENGTH.createValue(factory, qualifier);
|
||||
private static List<DfaMemoryState> indexOf(DfaValue qualifier,
|
||||
DfaMemoryState memState,
|
||||
DfaValueFactory factory,
|
||||
SpecialField specialField) {
|
||||
DfaValue length = specialField.createValue(factory, qualifier);
|
||||
LongRangeSet range = memState.getRange(length);
|
||||
long maxLen = range == null || range.isEmpty() ? Integer.MAX_VALUE : range.max();
|
||||
return singleResult(memState, factory.getRangeFactory().create(LongRangeSet.range(-1, maxLen - 1)));
|
||||
|
||||
+34
-3
@@ -30,6 +30,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.intellij.codeInspection.dataFlow.MethodContract.ValueConstraint.*;
|
||||
import static com.intellij.codeInspection.dataFlow.StandardMethodContract.createConstraintArray;
|
||||
@@ -38,6 +39,9 @@ import static com.intellij.codeInspection.dataFlow.StandardMethodContract.create
|
||||
* @author peter
|
||||
*/
|
||||
public class HardcodedContracts {
|
||||
private static final Pattern FIRST_OR_LAST = Pattern.compile("first|last");
|
||||
private static final Pattern CONTAINS_KEY_VALUE = Pattern.compile("containsKey|containsValue");
|
||||
|
||||
public static List<MethodContract> getHardcodedContracts(@NotNull PsiMethod method, @Nullable PsiMethodCallExpression call) {
|
||||
PsiClass owner = method.getContainingClass();
|
||||
if (owner == null ||
|
||||
@@ -96,10 +100,37 @@ public class HardcodedContracts {
|
||||
else if (MethodUtils.methodMatches(method, CommonClassNames.JAVA_UTIL_COLLECTION, PsiType.BOOLEAN, "isEmpty")) {
|
||||
return SpecialField.COLLECTION_SIZE.getEmptyContracts();
|
||||
}
|
||||
else if (MethodUtils.methodMatches(method, CommonClassNames.JAVA_UTIL_COLLECTION, null, "get", PsiType.INT)) {
|
||||
else if (MethodUtils.methodMatches(method, CommonClassNames.JAVA_UTIL_COLLECTION, PsiType.BOOLEAN, "contains", (PsiType)null)) {
|
||||
return Collections.singletonList(MethodContract.singleConditionContract(
|
||||
ContractValue.qualifier().specialField(SpecialField.COLLECTION_SIZE), RelationType.EQ, ContractValue.zero(), FALSE_VALUE));
|
||||
}
|
||||
else if (MethodUtils.methodMatches(method, CommonClassNames.JAVA_UTIL_SET, PsiType.BOOLEAN, "equals", (PsiType)null) ||
|
||||
MethodUtils.methodMatches(method, CommonClassNames.JAVA_UTIL_LIST, PsiType.BOOLEAN, "equals", (PsiType)null)) {
|
||||
return Collections.singletonList(MethodContract.singleConditionContract(
|
||||
ContractValue.qualifier().specialField(SpecialField.COLLECTION_SIZE), RelationType.NE,
|
||||
ContractValue.argument(0).specialField(SpecialField.COLLECTION_SIZE), FALSE_VALUE));
|
||||
}
|
||||
else if (MethodUtils.methodMatches(method, CommonClassNames.JAVA_UTIL_LIST, null, "get", PsiType.INT)) {
|
||||
return Arrays.asList(nonnegativeArgumentContract(0),
|
||||
specialFieldRangeContract(0, RelationType.LT, SpecialField.COLLECTION_SIZE));
|
||||
}
|
||||
else if (MethodUtils.methodMatches(method, "java.util.SortedSet", null, FIRST_OR_LAST)) {
|
||||
return Collections.singletonList(MethodContract.singleConditionContract(
|
||||
ContractValue.qualifier().specialField(SpecialField.COLLECTION_SIZE), RelationType.EQ,
|
||||
ContractValue.zero(), THROW_EXCEPTION));
|
||||
}
|
||||
else if (MethodUtils.methodMatches(method, CommonClassNames.JAVA_UTIL_MAP, PsiType.BOOLEAN, "isEmpty")) {
|
||||
return SpecialField.MAP_SIZE.getEmptyContracts();
|
||||
}
|
||||
else if (MethodUtils.methodMatches(method, CommonClassNames.JAVA_UTIL_MAP, PsiType.BOOLEAN, CONTAINS_KEY_VALUE, (PsiType)null)) {
|
||||
return Collections.singletonList(MethodContract.singleConditionContract(
|
||||
ContractValue.qualifier().specialField(SpecialField.MAP_SIZE), RelationType.EQ, ContractValue.zero(), FALSE_VALUE));
|
||||
}
|
||||
else if (MethodUtils.methodMatches(method, CommonClassNames.JAVA_UTIL_MAP, PsiType.BOOLEAN, "equals", (PsiType)null)) {
|
||||
return Collections.singletonList(MethodContract.singleConditionContract(
|
||||
ContractValue.qualifier().specialField(SpecialField.MAP_SIZE), RelationType.NE,
|
||||
ContractValue.argument(0).specialField(SpecialField.MAP_SIZE), FALSE_VALUE));
|
||||
}
|
||||
else if ("org.apache.commons.lang.Validate".equals(className) ||
|
||||
"org.apache.commons.lang3.Validate".equals(className) ||
|
||||
"org.springframework.util.Assert".equals(className)) {
|
||||
@@ -138,12 +169,12 @@ public class HardcodedContracts {
|
||||
|
||||
static MethodContract nonnegativeArgumentContract(int argNumber) {
|
||||
return MethodContract
|
||||
.singleConditionContract(ContractValue.argument(argNumber), RelationType.LT, ContractValue.constant(0, PsiType.INT), THROW_EXCEPTION);
|
||||
.singleConditionContract(ContractValue.argument(argNumber), RelationType.LT, ContractValue.zero(), THROW_EXCEPTION);
|
||||
}
|
||||
|
||||
static MethodContract specialFieldRangeContract(int index, RelationType type, SpecialField specialField) {
|
||||
return MethodContract.singleConditionContract(ContractValue.argument(index), type.getNegated(),
|
||||
ContractValue.specialField(ContractValue.qualifier(), specialField), THROW_EXCEPTION);
|
||||
ContractValue.qualifier().specialField(specialField), THROW_EXCEPTION);
|
||||
}
|
||||
|
||||
private static boolean isJunit(String className) {
|
||||
|
||||
@@ -40,7 +40,8 @@ public enum SpecialField {
|
||||
return obj instanceof String ? factory.getConstFactory().createFromValue(((String)obj).length(), PsiType.INT, null) : null;
|
||||
}
|
||||
},
|
||||
COLLECTION_SIZE(CommonClassNames.JAVA_UTIL_COLLECTION, "size", false, LongRangeSet.indexRange());
|
||||
COLLECTION_SIZE(CommonClassNames.JAVA_UTIL_COLLECTION, "size", false, LongRangeSet.indexRange()),
|
||||
MAP_SIZE(CommonClassNames.JAVA_UTIL_MAP, "size", false, LongRangeSet.indexRange());
|
||||
|
||||
private final String myClassName;
|
||||
private final String myMethodName;
|
||||
@@ -123,9 +124,8 @@ public enum SpecialField {
|
||||
* @return a list of method contracts which equivalent to checking this special field for zero
|
||||
*/
|
||||
public List<MethodContract> getEmptyContracts() {
|
||||
ContractValue thisValue = ContractValue.specialField(ContractValue.qualifier(), this);
|
||||
ContractValue zeroValue = ContractValue.constant(0, PsiType.INT);
|
||||
return Arrays.asList(MethodContract.singleConditionContract(thisValue, DfaRelationValue.RelationType.EQ, zeroValue,
|
||||
ContractValue thisValue = ContractValue.qualifier().specialField(this);
|
||||
return Arrays.asList(MethodContract.singleConditionContract(thisValue, DfaRelationValue.RelationType.EQ, ContractValue.zero(),
|
||||
MethodContract.ValueConstraint.TRUE_VALUE),
|
||||
MethodContract.trivialContract(MethodContract.ValueConstraint.FALSE_VALUE));
|
||||
}
|
||||
|
||||
@@ -223,4 +223,40 @@ public class LongRangeKnownMethods {
|
||||
}
|
||||
else return "";
|
||||
}
|
||||
|
||||
void testSetFirst(TreeSet<Integer> set) {
|
||||
if (set.first() == 0 && <warning descr="Condition 'set.size() > 0' is always 'true' when reached">set.size() > 0</warning>) {
|
||||
System.out.println("Impossible");
|
||||
}
|
||||
}
|
||||
|
||||
void testMap(HashMap<String, Integer> map) {
|
||||
if(<warning descr="Condition 'map.isEmpty() && map.containsKey(\"xyz\")' is always 'false'">map.isEmpty() && <warning descr="Condition 'map.containsKey(\"xyz\")' is always 'false' when reached">map.containsKey("xyz")</warning></warning>) {
|
||||
System.out.println("Impossible");
|
||||
}
|
||||
}
|
||||
|
||||
void testMapContainsValue(TreeMap<String, Integer> map) {
|
||||
if(<warning descr="Condition 'map.containsValue(1) && map.size() < 1' is always 'false'">map.containsValue(1) && <warning descr="Condition 'map.size() < 1' is always 'false' when reached">map.size() < 1</warning></warning>) {
|
||||
System.out.println("Impossible");
|
||||
}
|
||||
}
|
||||
|
||||
void testMapEquals(Map<String, String> map, Map<String, String> otherMap) {
|
||||
if(<warning descr="Condition 'map.isEmpty() && otherMap.equals(map) && otherMap.containsValue(\"xyz\")' is always 'false'">map.isEmpty() && otherMap.equals(map) && <warning descr="Condition 'otherMap.containsValue(\"xyz\")' is always 'false'">otherMap.containsValue("xyz")</warning></warning>) {
|
||||
System.out.println("Impossible");
|
||||
}
|
||||
}
|
||||
|
||||
void testListIndexOf(List<String> list) {
|
||||
if(<warning descr="Condition 'list.size() == 10 && list.indexOf(\"xyz\") == 15' is always 'false'">list.size() == 10 && <warning descr="Condition 'list.indexOf(\"xyz\") == 15' is always 'false' when reached">list.indexOf("xyz") == 15</warning></warning>) {
|
||||
System.out.println("Impossible");
|
||||
}
|
||||
}
|
||||
|
||||
void testGetUnknown(List<String> list, int index) {
|
||||
if(<warning descr="Condition 'list.get(index).isEmpty() && list.isEmpty()' is always 'false'">list.get(index).isEmpty() && <warning descr="Condition 'list.isEmpty()' is always 'false' when reached">list.isEmpty()</warning></warning>) {
|
||||
System.out.println("Impossible");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user