summaryrefslogtreecommitdiff
path: root/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInference.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInference.java')
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInference.java77
1 files changed, 61 insertions, 16 deletions
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInference.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInference.java
index 534d65b07531..a1c908837ad0 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInference.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInference.java
@@ -15,6 +15,7 @@
*/
package com.intellij.codeInspection.dataFlow;
+import com.intellij.codeInsight.NullableNotNullManager;
import com.intellij.codeInspection.dataFlow.MethodContract.ValueConstraint;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Condition;
@@ -41,6 +42,10 @@ public class ContractInference {
@NotNull
public static List<MethodContract> inferContracts(@NotNull final PsiMethod method) {
+ if (method instanceof PsiCompiledElement) {
+ return Collections.emptyList();
+ }
+
return CachedValuesManager.getCachedValue(method, new CachedValueProvider<List<MethodContract>>() {
@Nullable
@Override
@@ -70,7 +75,12 @@ class ContractInferenceInterpreter {
}
else if (statements[0] instanceof PsiExpressionStatement && ((PsiExpressionStatement)statements[0]).getExpression() instanceof PsiMethodCallExpression) {
List<MethodContract> result = handleDelegation(((PsiExpressionStatement)statements[0]).getExpression(), false);
- if (result != null) return result;
+ if (result != null) return ContainerUtil.findAll(result, new Condition<MethodContract>() {
+ @Override
+ public boolean value(MethodContract contract) {
+ return contract.returnValue == THROW_EXCEPTION || !textMatches(myMethod.getReturnTypeElement(), PsiKeyword.VOID);
+ }
+ });
}
}
@@ -103,7 +113,7 @@ class ContractInferenceInterpreter {
return RecursionManager.doPreventingRecursion(myMethod, true, new Computable<List<MethodContract>>() {
@Override
public List<MethodContract> compute() {
- List<MethodContract> delegateContracts = ContractInference.inferContracts(targetMethod); //todo use explicit contracts, too
+ List<MethodContract> delegateContracts = ControlFlowAnalyzer.getMethodContracts(targetMethod);
return ContainerUtil.mapNotNull(delegateContracts, new NullableFunction<MethodContract, MethodContract>() {
@Nullable
@Override
@@ -125,7 +135,7 @@ class ContractInferenceInterpreter {
}
}
}
- return new MethodContract(answer, negated ? negateConstraint(delegateContract.returnValue) : delegateContract.returnValue);
+ return answer == null ? null : new MethodContract(answer, negated ? negateConstraint(delegateContract.returnValue) : delegateContract.returnValue);
}
});
}
@@ -173,10 +183,12 @@ class ContractInferenceInterpreter {
if (expr instanceof PsiInstanceOfExpression) {
final int parameter = resolveParameter(((PsiInstanceOfExpression)expr).getOperand());
if (parameter >= 0) {
- return ContainerUtil.map(states, new Function<ValueConstraint[], MethodContract>() {
+ return ContainerUtil.mapNotNull(states, new Function<ValueConstraint[], MethodContract>() {
@Override
public MethodContract fun(ValueConstraint[] state) {
- return new MethodContract(withConstraint(state, parameter, NULL_VALUE), FALSE_VALUE);
+ ValueConstraint paramConstraint = NULL_VALUE;
+ ValueConstraint returnValue = FALSE_VALUE;
+ return contractWithConstraint(state, parameter, paramConstraint, returnValue);
}
});
}
@@ -187,17 +199,17 @@ class ContractInferenceInterpreter {
return toContracts(states, constraint);
}
- int parameter = resolveParameter(expr);
- if (parameter >= 0) {
+ int paramIndex = resolveParameter(expr);
+ if (paramIndex >= 0) {
List<MethodContract> result = ContainerUtil.newArrayList();
for (ValueConstraint[] state : states) {
- if (state[parameter] != ANY_VALUE) {
+ if (state[paramIndex] != ANY_VALUE) {
// the second 'o' reference in cases like: if (o != null) return o;
- result.add(new MethodContract(state, state[parameter]));
- } else {
+ result.add(new MethodContract(state, state[paramIndex]));
+ } else if (textMatches(myMethod.getParameterList().getParameters()[paramIndex].getTypeElement(), PsiKeyword.BOOLEAN)) {
// if (boolValue) ...
- result.add(new MethodContract(withConstraint(state, parameter, TRUE_VALUE), TRUE_VALUE));
- result.add(new MethodContract(withConstraint(state, parameter, FALSE_VALUE), FALSE_VALUE));
+ ContainerUtil.addIfNotNull(result, contractWithConstraint(state, paramIndex, TRUE_VALUE, TRUE_VALUE));
+ ContainerUtil.addIfNotNull(result, contractWithConstraint(state, paramIndex, FALSE_VALUE, FALSE_VALUE));
}
}
return result;
@@ -206,6 +218,18 @@ class ContractInferenceInterpreter {
return Collections.emptyList();
}
+ @Nullable
+ private MethodContract contractWithConstraint(ValueConstraint[] state,
+ int parameter, ValueConstraint paramConstraint,
+ ValueConstraint returnValue) {
+ ValueConstraint[] newState = withConstraint(state, parameter, paramConstraint);
+ return newState == null ? null : new MethodContract(newState, returnValue);
+ }
+
+ private static boolean textMatches(@Nullable PsiTypeElement typeElement, @NotNull String text) {
+ return typeElement != null && typeElement.textMatches(text);
+ }
+
private List<MethodContract> visitEqualityComparison(List<ValueConstraint[]> states,
PsiExpression op1,
PsiExpression op2,
@@ -219,8 +243,9 @@ class ContractInferenceInterpreter {
if (parameter >= 0 && constraint != null) {
List<MethodContract> result = ContainerUtil.newArrayList();
for (ValueConstraint[] state : states) {
- result.add(new MethodContract(withConstraint(state, parameter, constraint), equality ? TRUE_VALUE : FALSE_VALUE));
- result.add(new MethodContract(withConstraint(state, parameter, negateConstraint(constraint)), equality ? FALSE_VALUE : TRUE_VALUE));
+ ContainerUtil.addIfNotNull(result, contractWithConstraint(state, parameter, constraint, equality ? TRUE_VALUE : FALSE_VALUE));
+ ContainerUtil.addIfNotNull(result, contractWithConstraint(state, parameter, negateConstraint(constraint),
+ equality ? FALSE_VALUE : TRUE_VALUE));
}
return result;
}
@@ -295,7 +320,15 @@ class ContractInferenceInterpreter {
result.addAll(toContracts(states, THROW_EXCEPTION));
}
else if (statement instanceof PsiReturnStatement) {
- result.addAll(visitExpression(states, ((PsiReturnStatement)statement).getReturnValue()));
+ List<MethodContract> contracts = visitExpression(states, ((PsiReturnStatement)statement).getReturnValue());
+ for (MethodContract contract : contracts) {
+ if ((contract.returnValue == TRUE_VALUE || contract.returnValue == FALSE_VALUE) &&
+ !textMatches(myMethod.getReturnTypeElement(), PsiKeyword.BOOLEAN)) {
+ continue;
+ }
+
+ result.add(contract);
+ }
}
else if (statement instanceof PsiAssertStatement) {
List<MethodContract> conditionResults = visitExpression(states, ((PsiAssertStatement)statement).getAssertCondition());
@@ -357,7 +390,19 @@ class ContractInferenceInterpreter {
return -1;
}
- private static ValueConstraint[] withConstraint(ValueConstraint[] constraints, int index, ValueConstraint constraint) {
+ @Nullable
+ private ValueConstraint[] withConstraint(ValueConstraint[] constraints, int index, ValueConstraint constraint) {
+ if (constraints[index] == constraint) return constraints;
+
+ ValueConstraint negated = negateConstraint(constraint);
+ if (negated != constraint && constraints[index] == negated) {
+ return null;
+ }
+
+ if (constraint == NULL_VALUE && NullableNotNullManager.isNotNull(myMethod.getParameterList().getParameters()[index])) {
+ return null;
+ }
+
ValueConstraint[] copy = constraints.clone();
copy[index] = constraint;
return copy;