From 96c5bbef2f1eb9d433b9cab24ad56afeb12fdbe2 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Fri, 29 Sep 2017 17:30:43 +0700 Subject: [PATCH] SetInspectionOptionFix; used in DataFlowInspectionBase --- .../SetInspectionOptionFix.java | 48 +++++++++++ .../dataFlow/DataFlowInspectionBase.java | 80 +++++++++++++------ .../src/messages/InspectionsBundle.properties | 5 ++ 3 files changed, 107 insertions(+), 26 deletions(-) create mode 100644 java/java-analysis-impl/src/com/intellij/codeInspection/SetInspectionOptionFix.java diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/SetInspectionOptionFix.java b/java/java-analysis-impl/src/com/intellij/codeInspection/SetInspectionOptionFix.java new file mode 100644 index 000000000000..90d7c9d74ee0 --- /dev/null +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/SetInspectionOptionFix.java @@ -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); + }); + } +} diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java index d1762ac66b37..2043a69949d9 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java @@ -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 calls, - List qualifiers) { + List qualifiers, boolean isOnTheFly) { if (!REPORT_UNCHECKED_OPTIONALS) return; for (Map.Entry 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 reportedAnchors) { + private void reportConstantPushes(StandardDataFlowRunner runner, + ProblemsHolder holder, + Set 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 reportedAnchors) { + private void reportConstantReferenceValues(ProblemsHolder holder, + StandardInstructionVisitor visitor, + Set reportedAnchors, + boolean isOnTheFly) { for (Pair 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 #ref #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 #ref #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 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 reportedAnchors) { for (PsiElement expr : visitor.getProblems(NullabilityProblem.nullableFunctionReturn)) { if (!reportedAnchors.add(expr)) continue; diff --git a/platform/platform-resources-en/src/messages/InspectionsBundle.properties b/platform/platform-resources-en/src/messages/InspectionsBundle.properties index 12567a37ca6e..7e9dc017ef19 100644 --- a/platform/platform-resources-en/src/messages/InspectionsBundle.properties +++ b/platform/platform-resources-en/src/messages/InspectionsBundle.properties @@ -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 null 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 true +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