mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-130652 Check contract return type against method signature
This commit is contained in:
+6
-31
@@ -78,13 +78,15 @@ class ContractInferenceInterpreter {
|
||||
|
||||
List<MethodContract> 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<MethodContract>() {
|
||||
@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<MethodContract> 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<MethodContract>() {
|
||||
@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<MethodContract> result = handleDelegation(((PsiExpressionStatement)statements[0]).getExpression(), false);
|
||||
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);
|
||||
}
|
||||
});
|
||||
if (result != null) return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -385,15 +368,7 @@ class ContractInferenceInterpreter {
|
||||
result.addAll(toContracts(states, THROW_EXCEPTION));
|
||||
}
|
||||
else if (statement instanceof PsiReturnStatement) {
|
||||
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);
|
||||
}
|
||||
result.addAll(visitExpression(states, ((PsiReturnStatement)statement).getReturnValue()));
|
||||
}
|
||||
else if (statement instanceof PsiAssertStatement) {
|
||||
List<MethodContract> conditionResults = visitExpression(states, ((PsiAssertStatement)statement).getAssertCondition());
|
||||
|
||||
+23
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,4 +11,13 @@ class Foo {
|
||||
@Contract(pure=<warning descr="Pure methods must return something, void is not allowed as a return type">true</warning>)
|
||||
void voidPureMethod() {}
|
||||
|
||||
@Contract(<warning descr="Method returns void but the contract specifies null">"->null"</warning>)
|
||||
public native void throwMe();
|
||||
|
||||
@Contract(<warning descr="Method returns boolean but the contract specifies null">"->null"</warning>)
|
||||
public native boolean wrongReturnType();
|
||||
|
||||
@Contract(<warning descr="Method returns String but the contract specifies true">"->true"</warning>)
|
||||
public native String wrongReturnType2();
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user