SetInspectionOptionFix; used in DataFlowInspectionBase

This commit is contained in:
Tagir Valeev
2017-09-29 17:32:14 +07:00
parent d17a66cb3c
commit 96c5bbef2f
3 changed files with 107 additions and 26 deletions
@@ -0,0 +1,48 @@
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.codeInspection;
import com.intellij.codeInsight.intention.LowPriorityAction;
import com.intellij.codeInspection.ex.InspectionProfileModifiableModelKt;
import com.intellij.codeInspection.ex.InspectionToolWrapper;
import com.intellij.openapi.project.Project;
import com.intellij.util.ReflectionUtil;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
public class SetInspectionOptionFix implements LocalQuickFix, LowPriorityAction {
private String myShortName;
private final String myProperty;
private final String myMessage;
private boolean myValue;
public SetInspectionOptionFix(InspectionProfileEntry inspection, String property, String message, boolean value) {
myShortName = inspection.getShortName();
myProperty = property;
myMessage = message;
myValue = value;
}
@Nls
@NotNull
@Override
public String getName() {
return myMessage;
}
@Nls
@NotNull
@Override
public String getFamilyName() {
return "Set inspection option";
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
InspectionProfileModifiableModelKt.modifyAndCommitProjectProfile(project, model -> {
InspectionToolWrapper tool = model.getInspectionTool(myShortName, project);
if(tool == null) return;
InspectionProfileEntry inspection = tool.getTool();
ReflectionUtil.setField(inspection.getClass(), inspection, boolean.class, myProperty, myValue);
});
}
}
@@ -289,7 +289,7 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
reportAlwaysFailingCalls(holder, visitor, reportedAnchors);
reportConstantPushes(runner, holder, reportedAnchors);
reportConstantPushes(runner, holder, reportedAnchors, onTheFly);
reportNullableFunctions(visitor, holder, reportedAnchors);
reportNullableArguments(visitor, holder, reportedAnchors);
@@ -302,11 +302,11 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
reportOptionalOfNullableImprovements(holder, reportedAnchors, visitor.getOfNullableCalls());
reportUncheckedOptionalGet(holder, visitor.getOptionalCalls(), visitor.getOptionalQualifiers());
reportUncheckedOptionalGet(holder, visitor.getOptionalCalls(), visitor.getOptionalQualifiers(), onTheFly);
visitor.getBooleanCalls().forEach((call, state) -> {
if (state != ThreeState.UNSURE && reportedAnchors.add(call)) {
reportConstantCondition(holder, call, state.toBoolean());
reportConstantCondition(holder, call, state.toBoolean(), onTheFly);
}
});
@@ -315,11 +315,11 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
reportArrayAccessProblems(holder, visitor);
if (REPORT_CONSTANT_REFERENCE_VALUES) {
reportConstantReferenceValues(holder, visitor, reportedAnchors);
reportConstantReferenceValues(holder, visitor, reportedAnchors, onTheFly);
}
if (REPORT_NULLABLE_METHODS_RETURNING_NOT_NULL && visitor.isAlwaysReturnsNotNull(runner.getInstructions())) {
reportAlwaysReturnsNotNull(holder, scope);
reportAlwaysReturnsNotNull(holder, scope, onTheFly);
}
}
@@ -346,7 +346,7 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
private void reportUncheckedOptionalGet(ProblemsHolder holder,
Map<PsiMethodCallExpression, ThreeState> calls,
List<PsiExpression> qualifiers) {
List<PsiExpression> qualifiers, boolean isOnTheFly) {
if (!REPORT_UNCHECKED_OPTIONALS) return;
for (Map.Entry<PsiMethodCallExpression, ThreeState> entry : calls.entrySet()) {
ThreeState state = entry.getValue();
@@ -363,12 +363,15 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
// without deep correspondence analysis
continue;
}
LocalQuickFix fix = isOnTheFly ? new SetInspectionOptionFix(this, "REPORT_UNCHECKED_OPTIONALS", InspectionsBundle
.message("inspection.data.flow.turn.off.unchecked.optional.get.quickfix"), false) : null;
holder.registerProblem(getElementToHighlight(call),
InspectionsBundle.message("dataflow.message.optional.get.without.is.present", optionalClass.getName()));
InspectionsBundle.message("dataflow.message.optional.get.without.is.present", optionalClass.getName()),
fix);
}
}
private static void reportAlwaysReturnsNotNull(ProblemsHolder holder, PsiElement scope) {
private void reportAlwaysReturnsNotNull(ProblemsHolder holder, PsiElement scope, boolean isOnTheFly) {
if (!(scope.getParent() instanceof PsiMethod)) return;
PsiMethod method = (PsiMethod)scope.getParent();
@@ -381,7 +384,15 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
assert annoName != null;
String msg = "@" + NullableStuffInspectionBase.getPresentableAnnoName(nullableAnno) +
" method '" + method.getName() + "' always returns a non-null value";
holder.registerProblem(annoName, msg, new AddNotNullAnnotationFix(method));
LocalQuickFix[] fixes = {new AddNotNullAnnotationFix(method)};
if (isOnTheFly) {
fixes = ArrayUtil.append(fixes, new SetInspectionOptionFix(this, "REPORT_NULLABLE_METHODS_RETURNING_NOT_NULL",
InspectionsBundle
.message(
"inspection.data.flow.turn.off.nullable.returning.notnull.quickfix"),
false));
}
holder.registerProblem(annoName, msg, fixes);
}
private static void reportAlwaysFailingCalls(ProblemsHolder holder,
@@ -417,14 +428,17 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
return call;
}
private void reportConstantPushes(StandardDataFlowRunner runner, ProblemsHolder holder, Set<PsiElement> reportedAnchors) {
private void reportConstantPushes(StandardDataFlowRunner runner,
ProblemsHolder holder,
Set<PsiElement> reportedAnchors,
boolean isOnTheFly) {
for (Instruction instruction : runner.getInstructions()) {
if (instruction instanceof PushInstruction) {
PsiExpression place = ((PushInstruction)instruction).getPlace();
DfaValue value = ((PushInstruction)instruction).getValue();
Object constant = value instanceof DfaConstValue ? ((DfaConstValue)value).getValue() : null;
if (place instanceof PsiPolyadicExpression && constant instanceof Boolean && !isFlagCheck(place) && reportedAnchors.add(place)) {
reportConstantCondition(holder, place, (Boolean)constant);
reportConstantCondition(holder, place, (Boolean)constant, isOnTheFly);
}
}
}
@@ -451,7 +465,10 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
});
}
private static void reportConstantReferenceValues(ProblemsHolder holder, StandardInstructionVisitor visitor, Set<PsiElement> reportedAnchors) {
private void reportConstantReferenceValues(ProblemsHolder holder,
StandardInstructionVisitor visitor,
Set<PsiElement> reportedAnchors,
boolean isOnTheFly) {
for (Pair<PsiReferenceExpression, DfaConstValue> pair : visitor.getConstantReferenceValues()) {
PsiReferenceExpression ref = pair.first;
if (ref.getParent() instanceof PsiReferenceExpression || !reportedAnchors.add(ref)) {
@@ -466,8 +483,15 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
continue;
}
holder.registerProblem(ref, "Value <code>#ref</code> #loc is always '" + presentableName + "'",
new ReplaceWithConstantValueFix(presentableName, exprText));
LocalQuickFix[] fixes = {new ReplaceWithConstantValueFix(presentableName, exprText)};
if (isOnTheFly) {
fixes = ArrayUtil.append(fixes, new SetInspectionOptionFix(this, "REPORT_CONSTANT_REFERENCE_VALUES",
InspectionsBundle
.message("inspection.data.flow.turn.off.constant.references.quickfix"),
false));
}
holder.registerProblem(ref, "Value <code>#ref</code> #loc is always '" + presentableName + "'", fixes);
}
}
@@ -580,13 +604,13 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
);
}
else {
reportConstantCondition(holder, psiAnchor, evaluatesToTrue);
reportConstantCondition(holder, psiAnchor, evaluatesToTrue, onTheFly);
}
reportedAnchors.add(psiAnchor);
}
}
private void reportConstantCondition(ProblemsHolder holder, PsiElement psiAnchor, boolean evaluatesToTrue) {
private void reportConstantCondition(ProblemsHolder holder, PsiElement psiAnchor, boolean evaluatesToTrue, boolean isOnTheFly) {
if (psiAnchor.getParent() instanceof PsiForeachStatement) {
// highlighted for-each iterated value means evaluatesToTrue == "collection is always empty"
if (!evaluatesToTrue) {
@@ -598,12 +622,20 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
InspectionsBundle.message("dataflow.message.loop.on.empty.array") :
InspectionsBundle.message("dataflow.message.loop.on.empty.collection"));
}
else if (!skipReportingConstantCondition(psiAnchor, evaluatesToTrue)) {
final LocalQuickFix fix = createSimplifyBooleanExpressionFix(psiAnchor, evaluatesToTrue);
String message = InspectionsBundle.message(isAtRHSOfBooleanAnd(psiAnchor) ?
"dataflow.message.constant.condition.when.reached" :
"dataflow.message.constant.condition", Boolean.toString(evaluatesToTrue));
holder.registerProblem(psiAnchor, message, fix);
else {
boolean isAssertion = isAssertionEffectively(psiAnchor, evaluatesToTrue);
if (!DONT_REPORT_TRUE_ASSERT_STATEMENTS || !isAssertion) {
List<LocalQuickFix> fixes = new ArrayList<>();
ContainerUtil.addIfNotNull(fixes, createSimplifyBooleanExpressionFix(psiAnchor, evaluatesToTrue));
if (isAssertion && isOnTheFly) {
fixes.add(new SetInspectionOptionFix(this, "DONT_REPORT_TRUE_ASSERT_STATEMENTS",
InspectionsBundle.message("inspection.data.flow.turn.off.true.asserts.quickfix"), true));
}
String message = InspectionsBundle.message(isAtRHSOfBooleanAnd(psiAnchor) ?
"dataflow.message.constant.condition.when.reached" :
"dataflow.message.constant.condition", Boolean.toString(evaluatesToTrue));
holder.registerProblem(psiAnchor, message, fixes.toArray(LocalQuickFix.EMPTY_ARRAY));
}
}
}
@@ -611,10 +643,6 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
return LocalQuickFix.EMPTY_ARRAY;
}
private boolean skipReportingConstantCondition(PsiElement psiAnchor, boolean evaluatesToTrue) {
return DONT_REPORT_TRUE_ASSERT_STATEMENTS && isAssertionEffectively(psiAnchor, evaluatesToTrue);
}
private static void reportNullableFunctions(DataFlowInstructionVisitor visitor, ProblemsHolder holder, Set<PsiElement> reportedAnchors) {
for (PsiElement expr : visitor.getProblems(NullabilityProblem.nullableFunctionReturn)) {
if (!reportedAnchors.add(expr)) continue;
@@ -44,6 +44,11 @@ inspection.data.flow.display.name=Constant conditions \\& exceptions
inspection.contract.display.name=Contract issues
inspection.data.flow.nullable.quickfix.option=Suggest @Nullable annotation for methods that may possibly return <code>null</code> and report nullable values passed to non-annotated parameters
inspection.data.flow.true.asserts.option=Don't report assertions with condition statically proven to be always <code>true</code>
inspection.data.flow.turn.off.true.asserts.quickfix=Don't report always true assertions
inspection.data.flow.turn.off.null.passed.notnull.parameter.quickfix=Don't report not-null required parameter with null-literal argument usages
inspection.data.flow.turn.off.constant.references.quickfix=Don't report values which are guaranteed to be constant
inspection.data.flow.turn.off.nullable.returning.notnull.quickfix=Don't report nullable methods which always return not-null value
inspection.data.flow.turn.off.unchecked.optional.get.quickfix=Don't report Optional.get() calls without previous isPresent check
inspection.data.flow.redundant.instanceof.quickfix=Replace with != null
inspection.data.flow.simplify.boolean.expression.quickfix=Simplify boolean expression
inspection.data.flow.simplify.to.assignment.quickfix.name=Simplify to normal assignment