IDEA-195683 'Invert boolean' should update contract annotations when applicable

This commit is contained in:
Tagir Valeev
2018-08-01 03:43:14 +07:00
parent d15293e018
commit b3e8be172f
5 changed files with 81 additions and 9 deletions
@@ -2,6 +2,9 @@
package com.intellij.codeInspection.dataFlow;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInsight.InferredAnnotationsManagerImpl;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiAnnotation;
import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiMethod;
@@ -11,6 +14,7 @@ import com.intellij.psi.util.CachedValuesManager;
import com.intellij.psi.util.PsiModificationTracker;
import com.siyeh.ig.psiutils.ExpressionUtils;
import com.siyeh.ig.psiutils.MethodCallUtils;
import one.util.streamex.StreamEx;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -66,6 +70,24 @@ public class JavaMethodContractUtil {
return getContractInfo(method).isExplicit();
}
/**
* Creates a new {@link PsiAnnotation} describing the updated contract. Only contract clauses are updated;
* purity and mutation signature (if exist) are left as is.
*
* @param annotation original annotation to update
* @param contracts new contracts
* @return new {@link PsiAnnotation} object which describes updated contracts or null if no annotation is required to represent
* the target contracts (i.e. contracts is empty, method has no mutation signature and is not marked as pure).
*/
@Nullable
public static PsiAnnotation updateContract(PsiAnnotation annotation, List<StandardMethodContract> contracts) {
boolean pure = Boolean.TRUE.equals(AnnotationUtil.getBooleanAttributeValue(annotation, "pure"));
String mutates = StringUtil.notNullize(AnnotationUtil.getStringAttributeValue(annotation, MutationSignature.ATTR_MUTATES));
String resultValue = StreamEx.of(contracts).joining("; ");
Project project = annotation.getProject();
return InferredAnnotationsManagerImpl.createContractAnnotation(project, pure, resultValue, mutates);
}
static class ContractInfo {
static final ContractInfo EMPTY = new ContractInfo(Collections.emptyList(), false, false, MutationSignature.UNKNOWN);
@@ -2,13 +2,10 @@
package com.intellij.refactoring.changeSignature;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInsight.InferredAnnotationsManagerImpl;
import com.intellij.codeInspection.dataFlow.ContractReturnValue;
import com.intellij.codeInspection.dataFlow.JavaMethodContractUtil;
import com.intellij.codeInspection.dataFlow.MutationSignature;
import com.intellij.codeInspection.dataFlow.StandardMethodContract;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiAnnotation;
import com.intellij.psi.PsiMethod;
import one.util.streamex.IntStreamEx;
@@ -62,11 +59,7 @@ public final class ContractConverter {
result.add(convertContract(contract, newToOldIndex, oldToNewIndex, oldParameterNames));
}
if (result.equals(contracts)) return annotation;
boolean pure = Boolean.TRUE.equals(AnnotationUtil.getBooleanAttributeValue(annotation, "pure"));
String mutates = StringUtil.notNullize(AnnotationUtil.getStringAttributeValue(annotation, MutationSignature.ATTR_MUTATES));
String resultValue = StreamEx.of(result).joining("; ");
Project project = method.getProject();
return InferredAnnotationsManagerImpl.createContractAnnotation(project, pure, resultValue, mutates);
return JavaMethodContractUtil.updateContract(annotation, result);
}
@NotNull
@@ -15,7 +15,11 @@
*/
package com.intellij.refactoring.invertBoolean;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInsight.daemon.impl.RecursiveCallLineMarkerProvider;
import com.intellij.codeInspection.dataFlow.ContractReturnValue;
import com.intellij.codeInspection.dataFlow.JavaMethodContractUtil;
import com.intellij.codeInspection.dataFlow.StandardMethodContract;
import com.intellij.ide.util.SuperMethodWarningUtil;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.editor.Editor;
@@ -30,12 +34,14 @@ import com.intellij.refactoring.util.CommonRefactoringUtil;
import com.intellij.refactoring.util.LambdaRefactoringUtil;
import com.intellij.usageView.UsageInfo;
import com.intellij.util.Query;
import java.util.HashSet;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.MultiMap;
import com.siyeh.ig.psiutils.BoolUtils;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
public class JavaInvertBooleanDelegate extends InvertBooleanDelegate {
@Override
@@ -199,6 +205,33 @@ public class JavaInvertBooleanDelegate extends InvertBooleanDelegate {
if (initializer != null) {
replaceWithNegatedExpression(initializer);
}
} else if (element instanceof PsiMethod) {
updateContract((PsiMethod)element);
}
}
private static void updateContract(@NotNull PsiMethod method) {
PsiAnnotation annotation = JavaMethodContractUtil.findContractAnnotation(method);
if (annotation == null || annotation.getOwner() != method.getModifierList()) return;
String text = AnnotationUtil.getStringAttributeValue(annotation, null);
if (text == null || text.trim().isEmpty()) return;
List<StandardMethodContract> contracts;
try {
contracts = StandardMethodContract.parseContract(text);
}
catch (StandardMethodContract.ParseException ignore) {
return;
}
List<StandardMethodContract> newContracts = ContainerUtil.map(contracts, contract -> {
ContractReturnValue value = contract.getReturnValue();
return value instanceof ContractReturnValue.BooleanReturnValue ?
contract.withReturnValue(((ContractReturnValue.BooleanReturnValue)value).negate()) :
contract;
});
if (newContracts.equals(contracts)) return;
PsiAnnotation newAnnotation = JavaMethodContractUtil.updateContract(annotation, newContracts);
if (newAnnotation != null) {
annotation.replace(newAnnotation);
}
}
@@ -0,0 +1,12 @@
import org.jetbrains.annotations.Contract;
class C {
@Contract(value = "!null, _ -> false; null, null -> false; null, !null -> true", pure = true)
boolean f<caret>oo(String s, String s2) {
return s == null && s2 != null;
}
void bar() {
foo();
}
}
@@ -0,0 +1,12 @@
import org.jetbrains.annotations.Contract;
class C {
@Contract(value = "!null, _ -> true; null, null -> true; null, !null -> false", pure = true)
boolean fooInverted(String s, String s2) {
return s != null || s2 == null;
}
void bar() {
fooInverted();
}
}