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 21dc78070f67..4ef9a1dbc08b 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 @@ -78,13 +78,15 @@ class ContractInferenceInterpreter { List inferContracts() { final boolean notNull = NullableNotNullManager.isNotNull(myMethod); + PsiTypeElement typeElement = myMethod.getReturnTypeElement(); + final PsiType returnType = typeElement == null ? null : typeElement.getType(); return ContainerUtil.filter(doInferContracts(), new Condition() { @Override public boolean value(MethodContract contract) { if (notNull && contract.returnValue == NOT_NULL_VALUE) { return false; } - return true; + return ContractInspection.isReturnTypeCompatible(returnType, contract.returnValue); } }); } @@ -98,31 +100,12 @@ class ContractInferenceInterpreter { if (statements[0] instanceof PsiReturnStatement) { List result = handleDelegation(((PsiReturnStatement)statements[0]).getReturnValue(), false); if (result != null) { - PsiTypeElement typeElement = myMethod.getReturnTypeElement(); - final boolean returningObject = typeElement == null || !(typeElement.getType() instanceof PsiClassType); - return ContainerUtil.findAll(result, new Condition() { - @Override - public boolean value(MethodContract contract) { - if ((contract.returnValue == NULL_VALUE || contract.returnValue == NOT_NULL_VALUE) && returningObject) { - return false; - } - if ((contract.returnValue == TRUE_VALUE || contract.returnValue == FALSE_VALUE) && !returningObject) { - return false; - } - - return true; - } - }); + return result; } } else if (statements[0] instanceof PsiExpressionStatement && ((PsiExpressionStatement)statements[0]).getExpression() instanceof PsiMethodCallExpression) { List result = handleDelegation(((PsiExpressionStatement)statements[0]).getExpression(), false); - if (result != null) return ContainerUtil.findAll(result, new Condition() { - @Override - public boolean value(MethodContract contract) { - return contract.returnValue == THROW_EXCEPTION || !textMatches(myMethod.getReturnTypeElement(), PsiKeyword.VOID); - } - }); + if (result != null) return result; } } @@ -385,15 +368,7 @@ class ContractInferenceInterpreter { result.addAll(toContracts(states, THROW_EXCEPTION)); } else if (statement instanceof PsiReturnStatement) { - List 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); - } + result.addAll(visitExpression(states, ((PsiReturnStatement)statement).getReturnValue())); } else if (statement instanceof PsiAssertStatement) { List conditionResults = visitExpression(states, ((PsiAssertStatement)statement).getAssertCondition()); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInspection.java index 463ca738121f..dc86cba28de2 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInspection.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInspection.java @@ -91,7 +91,30 @@ public class ContractInspection extends BaseJavaBatchLocalInspectionTool { if (contract.arguments.length != paramCount) { return "Method takes " + paramCount + " parameters, while contract clause number " + (i + 1) + " expects " + contract.arguments.length; } + PsiType returnType = method.getReturnType(); + if (returnType != null && !isReturnTypeCompatible(returnType, contract.returnValue)) { + return "Method returns " + returnType.getPresentableText() + " but the contract specifies " + contract.returnValue; + } } return null; } + + static boolean isReturnTypeCompatible(@Nullable PsiType returnType, @NotNull MethodContract.ValueConstraint returnValue) { + if (returnValue == MethodContract.ValueConstraint.ANY_VALUE || returnValue == MethodContract.ValueConstraint.THROW_EXCEPTION) { + return true; + } + if (PsiType.VOID.equals(returnType)) return false; + + if (PsiType.BOOLEAN.equals(returnType)) { + return returnValue == MethodContract.ValueConstraint.TRUE_VALUE || + returnValue == MethodContract.ValueConstraint.FALSE_VALUE; + } + + if (returnType instanceof PsiClassType) { + return returnValue == MethodContract.ValueConstraint.NULL_VALUE || + returnValue == MethodContract.ValueConstraint.NOT_NULL_VALUE; + } + + return true; + } } diff --git a/java/java-tests/testData/inspection/dataFlow/contractCheck/SignatureIssues.java b/java/java-tests/testData/inspection/dataFlow/contractCheck/SignatureIssues.java index 84c77511c7e8..412a4c820bbd 100644 --- a/java/java-tests/testData/inspection/dataFlow/contractCheck/SignatureIssues.java +++ b/java/java-tests/testData/inspection/dataFlow/contractCheck/SignatureIssues.java @@ -11,4 +11,13 @@ class Foo { @Contract(pure=true) void voidPureMethod() {} + @Contract("->null") + public native void throwMe(); + + @Contract("->null") + public native boolean wrongReturnType(); + + @Contract("->true") + public native String wrongReturnType2(); + }