honor vararg calls when inferring contracts (IDEA-148862)

This commit is contained in:
peter
2015-12-03 17:32:53 +01:00
parent 594b3f670b
commit 0ddd1243b9
6 changed files with 40 additions and 6 deletions
@@ -17,6 +17,7 @@ package com.intellij.codeInspection.dataFlow;
import com.intellij.codeInsight.NullableNotNullManager;
import com.intellij.codeInspection.dataFlow.MethodContract.ValueConstraint;
import com.intellij.codeInspection.dataFlow.instructions.MethodCallInstruction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Condition;
@@ -158,10 +159,14 @@ class ContractInferenceInterpreter {
}
private List<MethodContract> handleCallDelegation(PsiMethodCallExpression expression, final boolean negated) {
final PsiMethod targetMethod = expression.resolveMethod();
JavaResolveResult result = expression.resolveMethodGenerics();
final PsiMethod targetMethod = (PsiMethod)result.getElement();
if (targetMethod == null) return Collections.emptyList();
final PsiParameter[] parameters = targetMethod.getParameterList().getParameters();
final PsiExpression[] arguments = expression.getArgumentList().getExpressions();
final boolean varArgCall = MethodCallInstruction.isVarArgCall(targetMethod, result.getSubstitutor(), arguments, parameters);
final boolean notNull = NullableNotNullManager.isNotNull(targetMethod);
List<MethodContract> fromDelegate = ContainerUtil.mapNotNull(ControlFlowAnalyzer.getMethodContracts(targetMethod), new NullableFunction<MethodContract, MethodContract>() {
@Nullable
@@ -170,9 +175,15 @@ class ContractInferenceInterpreter {
ValueConstraint[] answer = myEmptyConstraints;
for (int i = 0; i < delegateContract.arguments.length; i++) {
if (i >= arguments.length) return null;
ValueConstraint argConstraint = delegateContract.arguments[i];
if (argConstraint != ANY_VALUE) {
if (varArgCall && i >= parameters.length - 1) {
if (argConstraint == NULL_VALUE) {
return null;
}
break;
}
int paramIndex = resolveParameter(arguments[i]);
if (paramIndex < 0) {
if (argConstraint != getLiteralConstraint(arguments[i])) {
@@ -200,11 +200,12 @@ public class StandardInstructionVisitor extends InstructionVisitor {
if (method == null || instruction.getContracts().isEmpty()) {
argValues = null;
} else {
int paramCount = method.getParameterList().getParametersCount();
PsiParameterList paramList = method.getParameterList();
int paramCount = paramList.getParametersCount();
if (paramCount == args.length || method.isVarArgs() && args.length >= paramCount - 1) {
argValues = new DfaValue[paramCount];
if (varargCall) {
argValues[paramCount - 1] = DfaUnknownValue.getInstance();
argValues[paramCount - 1] = runner.getFactory().createTypeValue(paramList.getParameters()[paramCount - 1].getType(), Nullness.NOT_NULL);
}
} else {
argValues = null;
@@ -108,7 +108,7 @@ public class MethodCallInstruction extends Instruction {
return map;
}
private static boolean isVarArgCall(PsiMethod method, PsiSubstitutor substitutor, PsiExpression[] args, PsiParameter[] parameters) {
public static boolean isVarArgCall(PsiMethod method, PsiSubstitutor substitutor, PsiExpression[] args, PsiParameter[] parameters) {
if (!method.isVarArgs()) {
return false;
}
@@ -0,0 +1,8 @@
class Test {
private String wrap(final String wrapWith, final String token) {
return join(wrapWith, token, wrapWith);
}
@org.jetbrains.annotations.Contract("null->null; !null->!null")
native static <T> String join(T... elements);
}
@@ -44,4 +44,6 @@ public class ContractCheckTest extends LightCodeInsightFixtureTestCase {
public void testVarargInferred() { doTest(); }
public void testDoubleParameter() { doTest(); }
public void testReturnPrimitiveArray() { doTest(); }
public void testPassingVarargsToDelegate() { doTest(); }
}
@@ -519,6 +519,18 @@ class Foo {{
assert ContractInference.inferContracts(method).collect { it as String } == [' -> fail']
}
public void "test vararg delegation"() {
def c = inferContracts("""
boolean delegating(Object o, Object o1) {
return smth(o, o1);
}
boolean smth(Object o, Object... o1) {
return o == null && o1 != null;
}
""")
assert c == ['!null, _ -> false', 'null, _ -> true']
}
private String inferContract(String method) {
return assertOneElement(inferContracts(method))
}