add "pure" attribute to @Contract (IDEA-107864)

This commit is contained in:
peter
2013-11-01 19:47:00 +01:00
parent 612c075886
commit f92ce9af06
8 changed files with 56 additions and 17 deletions

View File

@@ -42,7 +42,7 @@ import static com.intellij.codeInsight.ConditionChecker.Type.*;
import static com.intellij.codeInspection.dataFlow.MethodContract.ValueConstraint;
import static com.intellij.psi.CommonClassNames.*;
class ControlFlowAnalyzer extends JavaElementVisitor {
public class ControlFlowAnalyzer extends JavaElementVisitor {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.dataFlow.ControlFlowAnalyzer");
public static final String ORG_JETBRAINS_ANNOTATIONS_CONTRACT = Contract.class.getName();
private boolean myIgnoreAssertions;

View File

@@ -111,13 +111,21 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
if (method == null) return;
String text = AnnotationUtil.getStringAttributeValue(annotation, null);
if (text == null) return;
if (StringUtil.isNotEmpty(text)) {
String error = checkContract(method, text);
if (error != null) {
PsiAnnotationMemberValue value = annotation.findAttributeValue(null);
assert value != null;
holder.registerProblem(value, error);
return;
}
}
String error = checkContract(method, text);
if (error != null) {
PsiAnnotationMemberValue value = annotation.findAttributeValue(null);
if (Boolean.TRUE.equals(AnnotationUtil.getBooleanAttributeValue(annotation, "pure")) &&
PsiType.VOID.equals(method.getReturnType())) {
PsiAnnotationMemberValue value = annotation.findDeclaredAttributeValue("pure");
assert value != null;
holder.registerProblem(value, error);
holder.registerProblem(value, "Pure methods must return something, void is not allowed as a return type");
}
}
};

View File

@@ -46,4 +46,7 @@ public class AssertIsNotNull {
@Contract(<warning descr="Method takes 2 parameters, while contract clause number 1 expects 1">"null -> _"</warning>)
void wrongParameterCount(Object a, boolean b) {}
@Contract(pure=<warning descr="Pure methods must return something, void is not allowed as a return type">true</warning>)
void voidPureMethod() {}
}

View File

@@ -39,12 +39,6 @@ public class DataFlowInspectionTest extends LightCodeInsightFixtureTestCase {
return JAVA_1_7;
}
@Override
protected void setUp() throws Exception {
super.setUp();
myFixture.addClass("package org.jetbrains.annotations; public @interface Contract { String value(); }");
}
@Override
protected String getTestDataPath() {
return JavaTestUtil.getJavaTestDataPath() + "/inspection/dataFlow/fixture/";

View File

@@ -43,11 +43,19 @@ import java.lang.annotation.*;
* <code>@Contract("_, null -> null; _, !null -> !null")</code> - method returns null if its second argument is null and not-null otherwise<br/>
* <code>@Contract("true -> fail")</code> - a typical assertFalse method which throws an exception if <code>true</code> is passed to it<br/>
*
* @author peter
*/
@Documented
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.METHOD)
public @interface Contract {
String value();
/**
* Contains the contract clauses describing causal relations between call arguments and the returned value
*/
String value() default "";
/**
* Specifies if this method is pure, i.e. has no visible side effects. This may be used for more precise data flow analysis, and
* to check that the method's return value is actually used in the call place.
*/
boolean pure() default false;
}

View File

@@ -15,6 +15,8 @@
*/
package com.siyeh.ig.bugs;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInspection.dataFlow.ControlFlowAnalyzer;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.psi.*;
@@ -146,6 +148,13 @@ public class IgnoreResultOfCallInspectionBase extends BaseInspection {
registerMethodCallError(call, aClass);
return;
}
PsiAnnotation contractAnnotation = ControlFlowAnalyzer.findContractAnnotation(method);
if (contractAnnotation != null && Boolean.TRUE.equals(AnnotationUtil.getBooleanAttributeValue(contractAnnotation, "pure"))) {
registerMethodCallError(call, aClass);
return;
}
final PsiReferenceExpression methodExpression = call.getMethodExpression();
final String methodName = methodExpression.getReferenceName();
if (methodName == null) {

View File

@@ -12,15 +12,15 @@ public class IgnoreResultOfCallInspectionTest extends LightInspectionTestCase {
@Override
protected String[] getEnvironmentClasses() {
return new String[]{
return [
"package java.util.regex; public class Pattern {" +
" public static Pattern compile(String regex) {return null;}" +
" public Matcher matcher(CharSequence input) {return null;}" +
"}",
"package java.util.regex; public class Matcher {" +
" public boolean find() {return true;}" +
"}",
};
"}"
] as String[]
}
public void testObjectMethods() {
@@ -41,4 +41,21 @@ public class IgnoreResultOfCallInspectionTest extends LightInspectionTestCase {
" }\n" +
"}\n");
}
public void testPureMethod() {
doTest """
import org.jetbrains.annotations.Contract;
class Util {
@Contract(pure=true)
static Object util() { return null; }
}
class C {
{
Util./*Result of 'Util.util()' is ignored*/util/**/();
}
}
"""
}
}