diff options
author | Tor Norbye <tnorbye@google.com> | 2014-07-25 13:10:36 -0700 |
---|---|---|
committer | Tor Norbye <tnorbye@google.com> | 2014-07-25 13:10:36 -0700 |
commit | e5266e2343c8d275d79fa0be725180d0fe3a993c (patch) | |
tree | 0ba72f5de1949e0527874a799baa224cbe1537e0 /java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInference.java | |
parent | b03a5855292feb8c331815f883fe64372aacd872 (diff) | |
parent | 2e5965e996aad62ab1338b09d54caaf99ff3dd6a (diff) | |
download | idea-e5266e2343c8d275d79fa0be725180d0fe3a993c.tar.gz |
Merge remote-tracking branch 'aosp/upstream-master' into merge
Conflicts:
.idea/modules.xml
Change-Id: I5e3d04bc83cdc26b2b56fca66b44b1dd8941b143
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.java | 77 |
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; |