DFA: Move equals handling to hardcoded contracts

This commit is contained in:
Tagir Valeev
2017-12-04 17:05:35 +07:00
parent 28f5ae80c0
commit eae5712fc0
4 changed files with 19 additions and 58 deletions
@@ -20,7 +20,6 @@ import com.intellij.codeInsight.ExceptionUtil;
import com.intellij.codeInspection.dataFlow.inliner.*;
import com.intellij.codeInspection.dataFlow.instructions.*;
import com.intellij.codeInspection.dataFlow.value.*;
import com.intellij.codeInspection.dataFlow.value.DfaRelationValue.RelationType;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.registry.Registry;
@@ -883,23 +882,6 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
ifNoException.setOffset(myCurrentFlow.getInstructionCount());
}
private static class ApplyNotNullInstruction extends Instruction {
@Override
public DfaInstructionState[] accept(DataFlowRunner runner, DfaMemoryState state, InstructionVisitor visitor) {
DfaValue value = state.pop();
DfaValueFactory factory = runner.getFactory();
if (state.applyCondition(factory.createCondition(value, RelationType.NE, factory.getConstFactory().getNull()))) {
return nextInstruction(runner, state);
}
return DfaInstructionState.EMPTY_ARRAY;
}
@Override
public String toString() {
return "APPLY NOT NULL";
}
}
@Override
public void visitTryStatement(PsiTryStatement statement) {
startElement(statement);
@@ -1499,10 +1481,6 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
JavaResolveResult result = methodExpression.advancedResolve(false);
PsiElement method = result.getElement();
PsiParameter[] parameters = method instanceof PsiMethod ? ((PsiMethod)method).getParameterList().getParameters() : null;
boolean isEqualsCall = expressions.length == 1 && method instanceof PsiMethod &&
"equals".equals(((PsiMethod)method).getName()) && parameters.length == 1 &&
parameters[0].getType().equalsToText(JAVA_LANG_OBJECT) &&
PsiType.BOOLEAN.equals(((PsiMethod)method).getReturnType());
for (int i = 0; i < expressions.length; i++) {
PsiExpression paramExpr = expressions[i];
@@ -1510,30 +1488,9 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
if (parameters != null && i < parameters.length) {
generateBoxingUnboxingInstructionFor(paramExpr, result.getSubstitutor().substitute(parameters[i].getType()));
}
if (i == 0 && isEqualsCall) {
// stack: .., qualifier, arg1
addInstruction(new SpliceInstruction(2, 0, 1, 0));
// stack: .., arg1, qualifier, arg1
}
}
addBareCall(expression, expression.getMethodExpression());
if (isEqualsCall) {
// assume equals argument must be not-null if the result is true
// don't assume the call result to be false if arg1==null
// stack: .., arg1, call-result
ConditionalGotoInstruction ifFalse = addInstruction(new ConditionalGotoInstruction(null, true, null));
addInstruction(new ApplyNotNullInstruction());
addInstruction(new PushInstruction(myFactory.getConstFactory().getTrue(), null));
addInstruction(new GotoInstruction(getEndOffset(expression)));
ifFalse.setOffset(myCurrentFlow.getInstructionCount());
addInstruction(new PopInstruction());
addInstruction(new PushInstruction(myFactory.getConstFactory().getFalse(), null));
}
finishElement(expression);
}
@@ -109,11 +109,7 @@ public class CustomMethodHandlers {
if (leftConst != null && rightConst != null) {
return singleResult(memState, factory.getBoolean(ignoreCase ? leftConst.equalsIgnoreCase(rightConst) : leftConst.equals(rightConst)));
}
DfaValue leftLength = SpecialField.STRING_LENGTH.createValue(factory, args.myQualifier);
DfaValue rightLength = SpecialField.STRING_LENGTH.createValue(factory, arg);
DfaValue trueRelation = factory.createCondition(leftLength, RelationType.EQ, rightLength);
DfaValue falseRelation = factory.createCondition(leftLength, RelationType.NE, rightLength);
return applyCondition(memState, trueRelation, DfaUnknownValue.getInstance(), falseRelation, factory.getBoolean(false));
return Collections.emptyList();
}
private static List<DfaMemoryState> indexOf(DfaValue qualifier,
@@ -110,6 +110,11 @@ public class HardcodedContracts {
else if ("isEmpty".equals(methodName) && paramCount == 0) {
return SpecialField.STRING_LENGTH.getEmptyContracts();
}
else if (("equals".equals(methodName) || "equalsIgnoreCase".equals(methodName)) && paramCount == 1) {
return Arrays.asList(getEqualsContract(), MethodContract.singleConditionContract(
ContractValue.qualifier().specialField(SpecialField.STRING_LENGTH), RelationType.NE,
ContractValue.argument(0).specialField(SpecialField.STRING_LENGTH), FALSE_VALUE));
}
}
else if (MethodUtils.methodMatches(method, CommonClassNames.JAVA_UTIL_COLLECTION, PsiType.BOOLEAN, "isEmpty")) {
return SpecialField.COLLECTION_SIZE.getEmptyContracts();
@@ -120,7 +125,7 @@ public class HardcodedContracts {
}
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(
return Arrays.asList(getEqualsContract(), MethodContract.singleConditionContract(
ContractValue.qualifier().specialField(SpecialField.COLLECTION_SIZE), RelationType.NE,
ContractValue.argument(0).specialField(SpecialField.COLLECTION_SIZE), FALSE_VALUE));
}
@@ -141,7 +146,7 @@ public class HardcodedContracts {
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(
return Arrays.asList(getEqualsContract(), MethodContract.singleConditionContract(
ContractValue.qualifier().specialField(SpecialField.MAP_SIZE), RelationType.NE,
ContractValue.argument(0).specialField(SpecialField.MAP_SIZE), FALSE_VALUE));
}
@@ -176,10 +181,18 @@ public class HardcodedContracts {
return Arrays.asList(optionalAbsentContract(FALSE_VALUE), MethodContract.trivialContract(TRUE_VALUE));
}
}
else if (MethodUtils.isEquals(method)) {
return Collections.singletonList(getEqualsContract());
}
return Collections.emptyList();
}
@NotNull
private static StandardMethodContract getEqualsContract() {
return new StandardMethodContract(new MethodContract.ValueConstraint[] {NULL_VALUE}, FALSE_VALUE);
}
static MethodContract optionalAbsentContract(MethodContract.ValueConstraint returnValue) {
return MethodContract
.singleConditionContract(ContractValue.qualifier(), RelationType.IS, ContractValue.optionalValue(false), returnValue);
@@ -324,15 +324,10 @@ public class StandardInstructionVisitor extends InstructionVisitor {
private List<DfaMemoryState> handleKnownMethods(MethodCallInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
CustomMethodHandlers.CustomMethodHandler handler = CustomMethodHandlers.find(instruction);
if (handler == null) return Collections.emptyList();
memState = memState.createCopy();
DfaCallArguments callArguments = popCall(instruction, runner, memState, false);
List<DfaMemoryState> states =
callArguments.myArguments == null ? Collections.emptyList() :
handler.handle(callArguments, memState, runner.getFactory());
if (states.isEmpty()) {
memState.push(getMethodResultValue(instruction, callArguments.myQualifier, memState, runner.getFactory()));
return Collections.singletonList(memState);
}
return states;
return callArguments.myArguments == null ? Collections.emptyList() :
handler.handle(callArguments, memState, runner.getFactory());
}
@NotNull