display hardcoded contracts on the gutter

This commit is contained in:
peter
2014-09-16 13:50:45 +02:00
parent a466800fd4
commit e2ee62224c
3 changed files with 66 additions and 18 deletions
@@ -17,12 +17,12 @@ package com.intellij.codeInsight;
import com.intellij.codeInspection.bytecodeAnalysis.ProjectBytecodeAnalysis;
import com.intellij.codeInspection.dataFlow.ContractInference;
import com.intellij.codeInspection.dataFlow.HardcodedContracts;
import com.intellij.codeInspection.dataFlow.MethodContract;
import com.intellij.codeInspection.dataFlow.PurityInference;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiAnnotation;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiModifierListOwner;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
@@ -33,14 +33,29 @@ import java.util.List;
import static com.intellij.codeInspection.dataFlow.ControlFlowAnalyzer.ORG_JETBRAINS_ANNOTATIONS_CONTRACT;
public class InferredAnnotationsManagerImpl extends InferredAnnotationsManager {
private final Project myProject;
public InferredAnnotationsManagerImpl(Project project) {
myProject = project;
}
@Nullable
@Override
public PsiAnnotation findInferredAnnotation(@NotNull PsiModifierListOwner listOwner, @NotNull String annotationFQN) {
listOwner = BaseExternalAnnotationsManager.preferCompiledElement(listOwner);
PsiAnnotation fromBytecode = ProjectBytecodeAnalysis.getInstance(listOwner.getProject()).findInferredAnnotation(listOwner, annotationFQN);
if (fromBytecode != null) {
return fromBytecode;
if (ORG_JETBRAINS_ANNOTATIONS_CONTRACT.equals(annotationFQN) && listOwner instanceof PsiMethod) {
PsiAnnotation anno = getHardcodedContractAnnotation((PsiMethod)listOwner);
if (anno != null) {
return anno;
}
}
if (!ignoreBytecodeInference(listOwner, annotationFQN)) {
PsiAnnotation fromBytecode = ProjectBytecodeAnalysis.getInstance(myProject).findInferredAnnotation(listOwner, annotationFQN);
if (fromBytecode != null) {
return fromBytecode;
}
}
if (ORG_JETBRAINS_ANNOTATIONS_CONTRACT.equals(annotationFQN) && canHaveContract(listOwner)) {
@@ -50,10 +65,34 @@ public class InferredAnnotationsManagerImpl extends InferredAnnotationsManager {
return null;
}
private PsiAnnotation getHardcodedContractAnnotation(PsiMethod listOwner) {
List<MethodContract> contracts = HardcodedContracts.getHardcodedContracts(listOwner, null);
return contracts.isEmpty() ? null : createContractAnnotation(contracts, true);
}
private static boolean ignoreBytecodeInference(PsiModifierListOwner owner, String annotationFQN) {
if (ORG_JETBRAINS_ANNOTATIONS_CONTRACT.equals(annotationFQN) && hasHardcodedContracts(owner)) {
return true;
}
if (AnnotationUtil.NOT_NULL.equals(annotationFQN) &&
owner instanceof PsiParameter && owner.getParent() != null &&
hasHardcodedContracts(owner.getParent().getParent())) {
return true;
}
return false;
}
private static boolean hasHardcodedContracts(PsiElement owner) {
return owner instanceof PsiMethod && !HardcodedContracts.getHardcodedContracts((PsiMethod)owner, null).isEmpty();
}
@Nullable
private static PsiAnnotation getInferredContractAnnotation(PsiMethod method) {
List<MethodContract> contracts = ContractInference.inferContracts((PsiMethod)method);
boolean pure = PurityInference.inferPurity(method);
private PsiAnnotation getInferredContractAnnotation(PsiMethod method) {
return createContractAnnotation(ContractInference.inferContracts(method), PurityInference.inferPurity(method));
}
@Nullable
private PsiAnnotation createContractAnnotation(List<MethodContract> contracts, boolean pure) {
final String attrs;
if (!contracts.isEmpty() && pure) {
attrs = "value = " + "\"" + StringUtil.join(contracts, "; ") + "\"; pure = true";
@@ -64,7 +103,7 @@ public class InferredAnnotationsManagerImpl extends InferredAnnotationsManager {
} else {
return null;
}
return ProjectBytecodeAnalysis.getInstance(method.getProject()).createContractAnnotation(attrs);
return ProjectBytecodeAnalysis.getInstance(myProject).createContractAnnotation(attrs);
}
private static boolean canHaveContract(PsiModifierListOwner listOwner) {
@@ -76,15 +115,18 @@ public class InferredAnnotationsManagerImpl extends InferredAnnotationsManager {
public PsiAnnotation[] findInferredAnnotations(@NotNull PsiModifierListOwner listOwner) {
listOwner = BaseExternalAnnotationsManager.preferCompiledElement(listOwner);
List<PsiAnnotation> result = ContainerUtil.newArrayList();
PsiAnnotation[] fromBytecode = ProjectBytecodeAnalysis.getInstance(listOwner.getProject()).findInferredAnnotations(listOwner);
PsiAnnotation[] fromBytecode = ProjectBytecodeAnalysis.getInstance(myProject).findInferredAnnotations(listOwner);
for (PsiAnnotation annotation : fromBytecode) {
if (!ORG_JETBRAINS_ANNOTATIONS_CONTRACT.equals(annotation.getQualifiedName()) || canHaveContract(listOwner)) {
result.add(annotation);
if (!ignoreBytecodeInference(listOwner, annotation.getQualifiedName())) {
if (!ORG_JETBRAINS_ANNOTATIONS_CONTRACT.equals(annotation.getQualifiedName()) || canHaveContract(listOwner)) {
result.add(annotation);
}
}
}
if (canHaveContract(listOwner)) {
ContainerUtil.addIfNotNull(result, getInferredContractAnnotation((PsiMethod)listOwner));
PsiAnnotation hardcoded = getHardcodedContractAnnotation((PsiMethod)listOwner);
ContainerUtil.addIfNotNull(result, hardcoded != null ? hardcoded : getInferredContractAnnotation((PsiMethod)listOwner));
}
return result.isEmpty() ? PsiAnnotation.EMPTY_ARRAY : result.toArray(new PsiAnnotation[result.size()]);
@@ -21,6 +21,7 @@ import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiMethodCallExpression;
import com.siyeh.ig.psiutils.ExpressionUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.List;
@@ -31,8 +32,8 @@ import static com.intellij.codeInspection.dataFlow.MethodContract.createConstrai
/**
* @author peter
*/
class HardcodedContracts {
static List<MethodContract> getHardcodedContracts(@NotNull PsiMethod method, @NotNull PsiMethodCallExpression call) {
public class HardcodedContracts {
public static List<MethodContract> getHardcodedContracts(@NotNull PsiMethod method, @Nullable PsiMethodCallExpression call) {
PsiClass owner = method.getContainingClass();
if (owner == null) return Collections.emptyList();
@@ -92,8 +93,8 @@ class HardcodedContracts {
}
private static List<MethodContract> handleTestFrameworks(int paramCount, String className, String methodName,
@NotNull PsiMethodCallExpression call) {
if ("assertThat".equals(methodName)) {
@Nullable PsiMethodCallExpression call) {
if ("assertThat".equals(methodName) && call != null) {
PsiExpression[] args = call.getArgumentList().getExpressions();
if (args.length == paramCount) {
for (int i = 1; i < args.length; i++) {
@@ -1557,6 +1557,11 @@
<item name="java.lang.System void checkKey(java.lang.String) 0">
<annotation name="org.jetbrains.annotations.NotNull"/>
</item>
<item name="java.lang.System void exit(int)">
<annotation name="org.jetbrains.annotations.Contract">
<val val="&quot;_ -&gt; fail&quot;"/>
</annotation>
</item>
<item name="java.lang.System void setProperties(java.util.Properties) 0">
<annotation name="org.jetbrains.annotations.Nullable"/>
</item>