[java-dfa] Extract "Constant conditions" into separate "Constant values" inspection (IDEA-58235)

GitOrigin-RevId: 95a81fcd1546afec31afc2a044a9ba5fa1337411
This commit is contained in:
Tagir Valeev
2022-09-07 15:25:09 +02:00
committed by intellij-monorepo-bot
parent 339e3a45d4
commit 6ffb7e417e
57 changed files with 1008 additions and 823 deletions

View File

@@ -3,12 +3,11 @@ package com.intellij.codeInspection.dataFlow;
import com.intellij.codeInspection.dataFlow.interpreter.RunnerResult;
import com.intellij.codeInspection.dataFlow.java.JavaDfaListener;
import com.intellij.codeInspection.dataFlow.java.anchor.JavaDfaAnchor;
import com.intellij.codeInspection.dataFlow.java.anchor.JavaExpressionAnchor;
import com.intellij.codeInspection.dataFlow.java.anchor.JavaMethodReferenceArgumentAnchor;
import com.intellij.codeInspection.dataFlow.java.anchor.*;
import com.intellij.codeInspection.dataFlow.jvm.SpecialField;
import com.intellij.codeInspection.dataFlow.jvm.descriptors.AssertionDisabledDescriptor;
import com.intellij.codeInspection.dataFlow.jvm.problems.ContractFailureProblem;
import com.intellij.codeInspection.dataFlow.lang.DfaAnchor;
import com.intellij.codeInspection.dataFlow.lang.UnsatisfiedConditionProblem;
import com.intellij.codeInspection.dataFlow.memory.DfaMemoryState;
import com.intellij.codeInspection.dataFlow.rangeSet.LongRangeSet;
@@ -220,6 +219,12 @@ public final class CommonDataflow {
return point == null ? DfType.TOP : point.myDfType;
}
@NotNull
public DfType getDfTypeNoAssertions(@NotNull JavaDfaAnchor anchor) {
DataflowPoint point = myDataAssertionsDisabled.get(anchor);
return point == null ? DfType.TOP : point.myDfType;
}
/**
* @param expression an expression to infer the DfType, must be deparenthesized.
* @return DfType for that expression, assuming assertions are disabled.
@@ -315,9 +320,20 @@ public final class CommonDataflow {
*/
@NotNull
public static DfType getDfType(PsiExpression expression) {
return getDfType(expression, false);
}
/**
* @param expression an expression to infer the DfType
* @param ignoreAssertions whether to ignore assertion statement during the analysis
* @return DfType for that expression. May return {@link DfType#TOP} if no information from dataflow is known about this expression
*/
@NotNull
public static DfType getDfType(PsiExpression expression, boolean ignoreAssertions) {
DataflowResult result = getDataflowResult(expression);
if (result == null) return DfType.TOP;
return result.getDfType(PsiUtil.skipParenthesizedExprDown(expression));
expression = PsiUtil.skipParenthesizedExprDown(expression);
return ignoreAssertions ? result.getDfTypeNoAssertions(expression) : result.getDfType(expression);
}
/**
@@ -371,10 +387,15 @@ public final class CommonDataflow {
}
@Override
public void beforeMethodReferenceArgumentPush(@NotNull DfaValue value,
@NotNull PsiMethodReferenceExpression expression,
@NotNull DfaMemoryState state) {
myResult.add(new JavaMethodReferenceArgumentAnchor(expression), state, value);
public void beforePush(@NotNull DfaValue @NotNull [] args,
@NotNull DfaValue value,
@NotNull DfaAnchor anchor,
@NotNull DfaMemoryState state) {
JavaDfaListener.super.beforePush(args, value, anchor, state);
if (anchor instanceof JavaMethodReferenceArgumentAnchor || anchor instanceof JavaPolyadicPartAnchor ||
anchor instanceof JavaMethodReferenceReturnAnchor) {
myResult.add((JavaDfaAnchor)anchor, state, value);
}
}
@Override

View File

@@ -12,37 +12,32 @@ import com.intellij.codeInspection.dataFlow.fix.*;
import com.intellij.codeInspection.dataFlow.interpreter.RunnerResult;
import com.intellij.codeInspection.dataFlow.java.anchor.JavaExpressionAnchor;
import com.intellij.codeInspection.dataFlow.java.anchor.JavaMethodReferenceReturnAnchor;
import com.intellij.codeInspection.dataFlow.java.anchor.JavaPolyadicPartAnchor;
import com.intellij.codeInspection.dataFlow.lang.ir.ControlFlow;
import com.intellij.codeInspection.dataFlow.lang.ir.Instruction;
import com.intellij.codeInspection.dataFlow.memory.DfaMemoryState;
import com.intellij.codeInspection.dataFlow.types.DfType;
import com.intellij.codeInspection.dataFlow.types.DfTypes;
import com.intellij.codeInspection.dataFlow.value.DfaValue;
import com.intellij.codeInspection.nullable.NullableStuffInspectionBase;
import com.intellij.codeInspection.util.InspectionMessage;
import com.intellij.java.analysis.JavaAnalysisBundle;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiImplUtil;
import com.intellij.psi.impl.source.PsiFieldImpl;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.*;
import com.intellij.util.*;
import com.intellij.util.ArrayUtilRt;
import com.intellij.util.JavaPsiConstructorUtil;
import com.intellij.util.ThreeState;
import com.intellij.util.containers.ContainerUtil;
import com.siyeh.ig.bugs.EqualsWithItselfInspection;
import com.siyeh.ig.fixes.EqualsToEqualityFix;
import com.siyeh.ig.numeric.ComparisonToNaNInspection;
import com.siyeh.ig.psiutils.*;
import one.util.streamex.StreamEx;
import org.jdom.Element;
import org.jetbrains.annotations.*;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.PropertyKey;
import javax.swing.*;
import java.util.*;
@@ -51,13 +46,10 @@ import java.util.function.Consumer;
import static com.intellij.util.ObjectUtils.tryCast;
public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool {
static final Logger LOG = Logger.getInstance(DataFlowInspectionBase.class);
@NonNls private static final String SHORT_NAME = "ConstantConditions";
public boolean SUGGEST_NULLABLE_ANNOTATIONS;
public boolean DONT_REPORT_TRUE_ASSERT_STATEMENTS;
public boolean TREAT_UNKNOWN_MEMBERS_AS_NULLABLE;
public boolean IGNORE_ASSERT_STATEMENTS;
public boolean REPORT_CONSTANT_REFERENCE_VALUES = true;
public boolean REPORT_NULLS_PASSED_TO_NOT_NULL_PARAMETER = true;
public boolean REPORT_NULLABLE_METHODS_RETURNING_NOT_NULL = true;
public boolean REPORT_UNSOUND_WARNINGS = true;
@@ -70,13 +62,11 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec
@Override
public void writeSettings(@NotNull Element node) throws WriteExternalException {
node.addContent(new Element("option").setAttribute("name", "SUGGEST_NULLABLE_ANNOTATIONS").setAttribute("value", String.valueOf(SUGGEST_NULLABLE_ANNOTATIONS)));
node.addContent(new Element("option").setAttribute("name", "DONT_REPORT_TRUE_ASSERT_STATEMENTS").setAttribute("value", String.valueOf(DONT_REPORT_TRUE_ASSERT_STATEMENTS)));
// Preserved for serialization compatibility
node.addContent(new Element("option").setAttribute("name", "DONT_REPORT_TRUE_ASSERT_STATEMENTS").setAttribute("value", "false"));
if (IGNORE_ASSERT_STATEMENTS) {
node.addContent(new Element("option").setAttribute("name", "IGNORE_ASSERT_STATEMENTS").setAttribute("value", "true"));
}
if (!REPORT_CONSTANT_REFERENCE_VALUES) {
node.addContent(new Element("option").setAttribute("name", "REPORT_CONSTANT_REFERENCE_VALUES").setAttribute("value", "false"));
}
if (TREAT_UNKNOWN_MEMBERS_AS_NULLABLE) {
node.addContent(new Element("option").setAttribute("name", "TREAT_UNKNOWN_MEMBERS_AS_NULLABLE").setAttribute("value", "true"));
}
@@ -152,25 +142,6 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec
}
}
}
@Override
public void visitIfStatement(@NotNull PsiIfStatement statement) {
PsiExpression condition = PsiUtil.skipParenthesizedExprDown(statement.getCondition());
if (BoolUtils.isBooleanLiteral(condition)) {
LocalQuickFix fix = createSimplifyBooleanExpressionFix(condition, condition.textMatches(PsiKeyword.TRUE));
holder.registerProblem(condition, JavaAnalysisBundle
.message("dataflow.message.constant.no.ref", condition.textMatches(PsiKeyword.TRUE) ? 1 : 0), fix);
}
}
@Override
public void visitDoWhileStatement(@NotNull PsiDoWhileStatement statement) {
PsiExpression condition = PsiUtil.skipParenthesizedExprDown(statement.getCondition());
if (condition != null && condition.textMatches(PsiKeyword.FALSE)) {
holder.registerProblem(condition, JavaAnalysisBundle.message("dataflow.message.constant.no.ref", 0),
createSimplifyBooleanExpressionFix(condition, false));
}
}
};
}
@@ -262,32 +233,25 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec
return null;
}
protected LocalQuickFix createReplaceWithTrivialLambdaFix(Object value) {
return null;
}
private void createDescription(ProblemsHolder holder,
final DataFlowInstructionVisitor visitor,
PsiElement scope,
Instruction @NotNull [] instructions) {
ProblemReporter reporter = new ProblemReporter(holder, scope);
Map<PsiExpression, ConstantResult> constantExpressions = visitor.getConstantExpressions();
reportFailingCasts(reporter, visitor, constantExpressions);
reportFailingCasts(reporter, visitor);
reportUnreachableSwitchBranches(visitor.getSwitchLabelsReachability(), holder);
reportAlwaysFailingCalls(reporter, visitor);
List<NullabilityProblem<?>> problems = NullabilityProblemKind.postprocessNullabilityProblems(visitor.problems().toList());
reportNullabilityProblems(reporter, problems, constantExpressions);
reportNullableReturns(reporter, problems, constantExpressions, scope);
reportNullabilityProblems(reporter, problems);
reportNullableReturns(reporter, problems, scope);
reportOptionalOfNullableImprovements(reporter, visitor.getOfNullableCalls());
reportRedundantInstanceOf(visitor, reporter);
reportConstants(reporter, visitor);
reportArrayAccessProblems(holder, visitor);
reportArrayStoreProblems(holder, visitor);
@@ -432,143 +396,6 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec
return branchCount > 1;
}
private void reportConstants(ProblemReporter reporter, DataFlowInstructionVisitor visitor) {
visitor.getConstantExpressionChunks().forEach((anchor, result) -> {
if (result == ConstantResult.UNKNOWN) return;
Object value = result.value();
if (anchor instanceof JavaPolyadicPartAnchor) {
if (value instanceof Boolean) {
// report rare cases like a == b == c where "a == b" part is constant
String message = JavaAnalysisBundle.message("dataflow.message.constant.condition",
((Boolean)value).booleanValue() ? 1 : 0);
reporter.registerProblem(((JavaPolyadicPartAnchor)anchor).getExpression(),
((JavaPolyadicPartAnchor)anchor).getTextRange(), message);
// do not add to reported anchors if only part of expression was reported
}
}
else if (anchor instanceof JavaExpressionAnchor) {
PsiExpression expression = ((JavaExpressionAnchor)anchor).getExpression();
if (isCondition(expression)) {
if (value instanceof Boolean) {
reportConstantBoolean(reporter, expression, (Boolean)value);
}
}
else {
reportConstantReferenceValue(reporter, expression, result);
}
}
else if (anchor instanceof JavaMethodReferenceReturnAnchor) {
PsiMethodReferenceExpression methodRef = ((JavaMethodReferenceReturnAnchor)anchor).getMethodReferenceExpression();
PsiMethod method = tryCast(methodRef.resolve(), PsiMethod.class);
if (method != null && JavaMethodContractUtil.isPure(method)) {
List<StandardMethodContract> contracts = JavaMethodContractUtil.getMethodContracts(method);
if (contracts.isEmpty() || !contracts.get(0).isTrivial()) {
reporter.registerProblem(methodRef, JavaAnalysisBundle.message("dataflow.message.constant.method.reference", value),
createReplaceWithTrivialLambdaFix(value));
}
}
}
});
}
private static boolean isCondition(@NotNull PsiExpression expression) {
PsiType type = expression.getType();
if (type == null || !PsiType.BOOLEAN.isAssignableFrom(type)) return false;
if (!(expression instanceof PsiMethodCallExpression) && !(expression instanceof PsiReferenceExpression)) return true;
PsiElement parent = PsiUtil.skipParenthesizedExprUp(expression.getParent());
if (parent instanceof PsiStatement) return !(parent instanceof PsiReturnStatement);
if (parent instanceof PsiPolyadicExpression) {
IElementType tokenType = ((PsiPolyadicExpression)parent).getOperationTokenType();
return tokenType.equals(JavaTokenType.ANDAND) || tokenType.equals(JavaTokenType.OROR) ||
tokenType.equals(JavaTokenType.AND) || tokenType.equals(JavaTokenType.OR);
}
if (parent instanceof PsiConditionalExpression) {
return PsiTreeUtil.isAncestor(((PsiConditionalExpression)parent).getCondition(), expression, false);
}
return PsiUtil.isAccessedForWriting(expression);
}
private void reportConstantReferenceValue(ProblemReporter reporter, PsiExpression ref, ConstantResult constant) {
if (!REPORT_CONSTANT_REFERENCE_VALUES && ref instanceof PsiReferenceExpression) return;
if (shouldBeSuppressed(ref) || constant == ConstantResult.UNKNOWN) return;
List<LocalQuickFix> fixes = new SmartList<>();
String presentableName = constant.toString();
if (Integer.valueOf(0).equals(constant.value()) && !shouldReportZero(ref)) return;
if (constant.value() instanceof Boolean) {
fixes.add(createSimplifyBooleanExpressionFix(ref, (Boolean)constant.value()));
} else {
fixes.add(new ReplaceWithConstantValueFix(presentableName, presentableName));
}
Object value = constant.value();
boolean isAssertion = isAssertionEffectively(ref, constant);
if (isAssertion && DONT_REPORT_TRUE_ASSERT_STATEMENTS) return;
if (value instanceof Boolean) {
ContainerUtil.addIfNotNull(fixes, createReplaceWithNullCheckFix(ref, (Boolean)value));
}
if (reporter.isOnTheFly()) {
if (ref instanceof PsiReferenceExpression) {
fixes.add(new SetInspectionOptionFix(this, "REPORT_CONSTANT_REFERENCE_VALUES",
JavaAnalysisBundle.message("inspection.data.flow.turn.off.constant.references.quickfix"),
false));
}
if (isAssertion) {
fixes.add(new SetInspectionOptionFix(this, "DONT_REPORT_TRUE_ASSERT_STATEMENTS",
JavaAnalysisBundle.message("inspection.data.flow.turn.off.true.asserts.quickfix"), true));
}
}
ContainerUtil.addIfNotNull(fixes, createExplainFix(ref, new TrackingRunner.ValueDfaProblemType(value)));
ProblemHighlightType type;
String message;
if (ref instanceof PsiMethodCallExpression || ref instanceof PsiPolyadicExpression || ref instanceof PsiTypeCastExpression) {
type = ProblemHighlightType.GENERIC_ERROR_OR_WARNING;
message = JavaAnalysisBundle.message("dataflow.message.constant.expression", presentableName);
}
else {
type = ProblemHighlightType.WEAK_WARNING;
message = JavaAnalysisBundle.message("dataflow.message.constant.value", presentableName);
}
reporter.registerProblem(ref, message, type, fixes.toArray(LocalQuickFix.EMPTY_ARRAY));
}
private static boolean shouldReportZero(PsiExpression ref) {
if (ref instanceof PsiPolyadicExpression) {
if (PsiUtil.isConstantExpression(ref)) return false;
PsiPolyadicExpression polyadic = (PsiPolyadicExpression)ref;
IElementType tokenType = polyadic.getOperationTokenType();
if (tokenType.equals(JavaTokenType.ASTERISK)) {
PsiMethod method = PsiTreeUtil.getParentOfType(ref, PsiMethod.class, true, PsiLambdaExpression.class, PsiClass.class);
if (MethodUtils.isHashCode(method)) {
// Standard hashCode template generates int result = 0; result = result * 31 + ...;
// so annoying warnings might be produced there
return false;
}
}
}
else if (ref instanceof PsiMethodCallExpression) {
PsiMethodCallExpression call = (PsiMethodCallExpression)ref;
PsiExpression qualifier = call.getMethodExpression().getQualifierExpression();
if (PsiUtil.isConstantExpression(qualifier) &&
ContainerUtil.and(call.getArgumentList().getExpressions(), PsiUtil::isConstantExpression)) {
return false;
}
}
else if (ref instanceof PsiTypeCastExpression) {
PsiExpression operand = ((PsiTypeCastExpression)ref).getOperand();
return operand != null && TypeConversionUtil.isFloatOrDoubleType(operand.getType());
}
else {
return false;
}
PsiElement parent = PsiUtil.skipParenthesizedExprUp(ref.getParent());
PsiBinaryExpression binOp = tryCast(parent, PsiBinaryExpression.class);
if (binOp != null && ComparisonUtils.isEqualityComparison(binOp) &&
(ExpressionUtils.isZero(binOp.getLOperand()) || ExpressionUtils.isZero(binOp.getROperand()))) {
return false;
}
return true;
}
private static void reportPointlessSameArguments(ProblemReporter reporter, DataFlowInstructionVisitor visitor) {
visitor.pointlessSameArguments().forKeyValue((expr, eq) -> {
PsiElement name = expr.getReferenceNameElement();
@@ -581,16 +408,16 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec
return;
}
}
if (eq.firstArgEqualToResult) {
String message = eq.argsEqual ? JavaAnalysisBundle.message("dataflow.message.pointless.same.arguments") :
if (eq.firstArgEqualToResult()) {
String message = eq.argsEqual() ? JavaAnalysisBundle.message("dataflow.message.pointless.same.arguments") :
JavaAnalysisBundle.message("dataflow.message.pointless.same.argument.and.result", 1);
LocalQuickFix fix = expressions.length == 2 ? new ReplaceWithArgumentFix(expressions[0], 0) : null;
reporter.registerProblem(name, message, fix);
}
else if (eq.argsEqual) {
else if (eq.argsEqual()) {
reporter.registerProblem(name, JavaAnalysisBundle.message("dataflow.message.pointless.same.arguments"));
}
else if (eq.secondArgEqualToResult) {
else if (eq.secondArgEqualToResult()) {
LocalQuickFix fix = expressions.length == 2 ? new ReplaceWithArgumentFix(expressions[1], 1) : null;
reporter.registerProblem(name, JavaAnalysisBundle.message("dataflow.message.pointless.same.argument.and.result", 2), fix);
}
@@ -627,6 +454,9 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec
}
}
}
DfType value = CommonDataflow.getDfType(expr, IGNORE_ASSERT_STATEMENTS);
// reported by ConstantValueInspection
if (value == DfTypes.TRUE || value == DfTypes.FALSE) return;
String message = assignment != null && !assignment.getOperationTokenType().equals(JavaTokenType.EQ)
? JavaAnalysisBundle.message("dataflow.message.redundant.update")
: JavaAnalysisBundle.message("dataflow.message.redundant.assignment");
@@ -644,31 +474,30 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec
return null;
}
protected void reportNullabilityProblems(ProblemReporter reporter,
List<NullabilityProblem<?>> problems,
Map<PsiExpression, ConstantResult> expressions) {
protected void reportNullabilityProblems(ProblemReporter reporter, List<NullabilityProblem<?>> problems) {
for (NullabilityProblem<?> problem : problems) {
PsiExpression expression = problem.getDereferencedExpression();
boolean nullLiteral = ExpressionUtils.isNullLiteral(PsiUtil.skipParenthesizedExprDown(expression));
if (!REPORT_UNSOUND_WARNINGS) {
if (expression == null || !nullLiteral && expressions.get(expression) != ConstantResult.NULL) continue;
if (expression == null || !nullLiteral && CommonDataflow.getDfType(expression, IGNORE_ASSERT_STATEMENTS) != DfTypes.NULL) continue;
}
// Expression of null type: could be failed LVTI, skip it to avoid confusion
if (expression != null && !nullLiteral && PsiType.NULL.equals(expression.getType())) continue;
boolean alwaysNull = problem.isAlwaysNull(expressions);
boolean alwaysNull = problem.isAlwaysNull(IGNORE_ASSERT_STATEMENTS);
NullabilityProblemKind.innerClassNPE.ifMyProblem(problem, newExpression -> {
List<LocalQuickFix> fixes = createNPEFixes(newExpression.getQualifier(), newExpression, reporter.isOnTheFly(), alwaysNull);
reporter
.registerProblem(getElementToHighlight(newExpression), problem.getMessage(expressions), fixes.toArray(LocalQuickFix.EMPTY_ARRAY));
.registerProblem(getElementToHighlight(newExpression), problem.getMessage(IGNORE_ASSERT_STATEMENTS),
fixes.toArray(LocalQuickFix.EMPTY_ARRAY));
});
NullabilityProblemKind.callMethodRefNPE.ifMyProblem(problem, methodRef ->
reporter.registerProblem(methodRef, JavaAnalysisBundle.message("dataflow.message.npe.methodref.invocation"),
createMethodReferenceNPEFixes(methodRef, reporter.isOnTheFly()).toArray(LocalQuickFix.EMPTY_ARRAY)));
NullabilityProblemKind.callNPE.ifMyProblem(problem, call ->
reportCallMayProduceNpe(reporter, problem.getMessage(expressions), call, alwaysNull));
reportCallMayProduceNpe(reporter, problem.getMessage(IGNORE_ASSERT_STATEMENTS), call, alwaysNull));
NullabilityProblemKind.passingToNotNullParameter.ifMyProblem(problem, expr -> {
List<LocalQuickFix> fixes = createNPEFixes(expression, expression, reporter.isOnTheFly(), alwaysNull);
reporter.registerProblem(expression, problem.getMessage(expressions), fixes.toArray(LocalQuickFix.EMPTY_ARRAY));
reporter.registerProblem(expression, problem.getMessage(IGNORE_ASSERT_STATEMENTS), fixes.toArray(LocalQuickFix.EMPTY_ARRAY));
});
NullabilityProblemKind.passingToNotNullMethodRefParameter.ifMyProblem(problem, methodRef -> {
LocalQuickFix[] fixes = createMethodReferenceNPEFixes(methodRef, reporter.isOnTheFly()).toArray(LocalQuickFix.EMPTY_ARRAY);
@@ -681,13 +510,13 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec
NullabilityProblemKind.arrayAccessNPE.ifMyProblem(problem, arrayAccess -> {
LocalQuickFix[] fixes = createNPEFixes(arrayAccess.getArrayExpression(), arrayAccess, reporter.isOnTheFly(),
alwaysNull).toArray(LocalQuickFix.EMPTY_ARRAY);
reporter.registerProblem(arrayAccess, problem.getMessage(expressions), fixes);
reporter.registerProblem(arrayAccess, problem.getMessage(IGNORE_ASSERT_STATEMENTS), fixes);
});
NullabilityProblemKind.fieldAccessNPE.ifMyProblem(problem, element -> {
PsiElement parent = element.getParent();
PsiExpression fieldAccess = parent instanceof PsiReferenceExpression ? (PsiExpression)parent : element;
LocalQuickFix[] fix = createNPEFixes(element, fieldAccess, reporter.isOnTheFly(), alwaysNull).toArray(LocalQuickFix.EMPTY_ARRAY);
reporter.registerProblem(element, problem.getMessage(expressions), fix);
reporter.registerProblem(element, problem.getMessage(IGNORE_ASSERT_STATEMENTS), fix);
});
NullabilityProblemKind.unboxingNullable.ifMyProblem(problem, element -> {
PsiExpression anchor = expression;
@@ -696,32 +525,34 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec
}
if (anchor != null) {
LocalQuickFix[] fixes = createUnboxingNullableFixes(anchor, element, reporter.isOnTheFly()).toArray(LocalQuickFix.EMPTY_ARRAY);
reporter.registerProblem(anchor, problem.getMessage(expressions), fixes);
reporter.registerProblem(anchor, problem.getMessage(IGNORE_ASSERT_STATEMENTS), fixes);
}
});
NullabilityProblemKind.nullableFunctionReturn.ifMyProblem(
problem, expr -> reporter.registerProblem(expression == null ? expr : expression, problem.getMessage(expressions)));
Consumer<PsiExpression> reportNullability = expr -> reportNullabilityProblem(reporter, problem, expression, expressions);
problem, expr -> reporter.registerProblem(expression == null ? expr : expression, problem.getMessage(IGNORE_ASSERT_STATEMENTS)));
Consumer<PsiExpression> reportNullability = expr -> reportNullabilityProblem(reporter, problem, expression);
NullabilityProblemKind.assigningToNotNull.ifMyProblem(problem, reportNullability);
NullabilityProblemKind.storingToNotNullArray.ifMyProblem(problem, reportNullability);
if (SUGGEST_NULLABLE_ANNOTATIONS) {
NullabilityProblemKind.passingToNonAnnotatedMethodRefParameter.ifMyProblem(
problem, methodRef -> reportNullableArgumentPassedToNonAnnotatedMethodRef(reporter, expressions, problem, methodRef));
problem, methodRef -> reportNullableArgumentPassedToNonAnnotatedMethodRef(reporter, problem, methodRef));
NullabilityProblemKind.passingToNonAnnotatedParameter.ifMyProblem(
problem, top -> reportNullableArgumentsPassedToNonAnnotated(reporter, problem.getMessage(expressions), expression, top, alwaysNull));
problem,
top -> reportNullableArgumentsPassedToNonAnnotated(reporter, problem.getMessage(IGNORE_ASSERT_STATEMENTS), expression, top,
alwaysNull));
NullabilityProblemKind.assigningToNonAnnotatedField.ifMyProblem(
problem, top -> reportNullableAssignedToNonAnnotatedField(reporter, top, expression, problem.getMessage(expressions), alwaysNull));
problem, top -> reportNullableAssignedToNonAnnotatedField(reporter, top, expression, problem.getMessage(IGNORE_ASSERT_STATEMENTS),
alwaysNull));
}
}
}
private void reportNullabilityProblem(ProblemReporter reporter,
NullabilityProblem<?> problem,
PsiExpression expr,
Map<PsiExpression, ConstantResult> expressions) {
LocalQuickFix[] fixes = createNPEFixes(expr, expr, reporter.isOnTheFly(), problem.isAlwaysNull(expressions))
PsiExpression expr) {
LocalQuickFix[] fixes = createNPEFixes(expr, expr, reporter.isOnTheFly(), problem.isAlwaysNull(IGNORE_ASSERT_STATEMENTS))
.toArray(LocalQuickFix.EMPTY_ARRAY);
reporter.registerProblem(expr, problem.getMessage(expressions), fixes);
reporter.registerProblem(expr, problem.getMessage(IGNORE_ASSERT_STATEMENTS), fixes);
}
private static void reportArrayAccessProblems(ProblemsHolder holder, DataFlowInstructionVisitor visitor) {
@@ -819,17 +650,16 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec
});
}
private static void reportNullableArgumentPassedToNonAnnotatedMethodRef(@NotNull ProblemReporter reporter,
@NotNull Map<PsiExpression, ConstantResult> expressions,
@NotNull NullabilityProblem<?> problem,
@NotNull PsiMethodReferenceExpression methodRef) {
private void reportNullableArgumentPassedToNonAnnotatedMethodRef(@NotNull ProblemReporter reporter,
@NotNull NullabilityProblem<?> problem,
@NotNull PsiMethodReferenceExpression methodRef) {
PsiMethod target = tryCast(methodRef.resolve(), PsiMethod.class);
if (target == null) return;
PsiParameter[] parameters = target.getParameterList().getParameters();
if (parameters.length == 0) return;
PsiParameter parameter = parameters[0];
if (!BaseIntentionAction.canModify(parameter) || !AnnotationUtil.isAnnotatingApplicable(parameter)) return;
reporter.registerProblem(methodRef, problem.getMessage(expressions),
reporter.registerProblem(methodRef, problem.getMessage(IGNORE_ASSERT_STATEMENTS),
parameters.length == 1 ? AddAnnotationPsiFix.createAddNullableFix(parameter) : null);
}
@@ -880,18 +710,17 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec
reporter.registerProblem(toHighlight, message, fixes.toArray(LocalQuickFix.EMPTY_ARRAY));
}
private void reportFailingCasts(@NotNull ProblemReporter reporter,
@NotNull DataFlowInstructionVisitor visitor,
@NotNull Map<PsiExpression, ConstantResult> constantExpressions) {
private void reportFailingCasts(@NotNull ProblemReporter reporter, @NotNull DataFlowInstructionVisitor visitor) {
visitor.getFailingCastExpressions().forKeyValue((typeCast, info) -> {
boolean alwaysFails = info.getFirst();
PsiType realType = info.getSecond();
if (!REPORT_UNSOUND_WARNINGS && !alwaysFails) return;
PsiExpression operand = typeCast.getOperand();
PsiTypeElement castType = typeCast.getCastType();
ConstantResult result = constantExpressions.get(PsiUtil.skipParenthesizedExprDown(operand));
// Skip reporting if cast operand is always null: null can be cast to anything
if (result == ConstantResult.NULL || ExpressionUtils.isNullLiteral(operand)) return;
if (ExpressionUtils.isNullLiteral(operand) || DfTypes.NULL.equals(CommonDataflow.getDfType(operand, IGNORE_ASSERT_STATEMENTS))) {
// Skip reporting if cast operand is always null: null can be cast to anything
return;
}
assert castType != null;
assert operand != null;
List<LocalQuickFix> fixes = new ArrayList<>(createCastFixes(typeCast, realType, reporter.isOnTheFly(), alwaysFails));
@@ -904,162 +733,22 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec
});
}
private void reportConstantBoolean(ProblemReporter reporter, PsiElement psiAnchor, boolean evaluatesToTrue) {
while (psiAnchor instanceof PsiParenthesizedExpression parenthesized) {
psiAnchor = parenthesized.getExpression();
}
if (psiAnchor == null || shouldBeSuppressed(psiAnchor)) return;
boolean isAssertion = psiAnchor instanceof PsiExpression expr && isAssertionEffectively(expr, evaluatesToTrue);
if (DONT_REPORT_TRUE_ASSERT_STATEMENTS && isAssertion) return;
if (PsiUtil.skipParenthesizedExprUp(psiAnchor.getParent()) instanceof PsiAssignmentExpression assignment &&
PsiTreeUtil.isAncestor(assignment.getLExpression(), psiAnchor, false)) {
reporter.registerProblem(
psiAnchor,
JavaAnalysisBundle.message("dataflow.message.pointless.assignment.expression", Boolean.toString(evaluatesToTrue)),
createConditionalAssignmentFixes(evaluatesToTrue, assignment, reporter.isOnTheFly())
);
return;
}
List<LocalQuickFix> fixes = new ArrayList<>();
if (!isCoveredBySurroundingFix(psiAnchor, evaluatesToTrue)) {
ContainerUtil.addIfNotNull(fixes, createSimplifyBooleanExpressionFix(psiAnchor, evaluatesToTrue));
if (isAssertion && reporter.isOnTheFly()) {
fixes.add(new SetInspectionOptionFix(this, "DONT_REPORT_TRUE_ASSERT_STATEMENTS",
JavaAnalysisBundle.message("inspection.data.flow.turn.off.true.asserts.quickfix"), true));
}
ContainerUtil.addIfNotNull(fixes, createReplaceWithNullCheckFix(psiAnchor, evaluatesToTrue));
}
if (psiAnchor instanceof PsiExpression) {
ContainerUtil.addIfNotNull(fixes, createExplainFix(
(PsiExpression)psiAnchor, new TrackingRunner.ValueDfaProblemType(evaluatesToTrue)));
}
String message = JavaAnalysisBundle.message(isAtRHSOfBooleanAnd(psiAnchor) ?
"dataflow.message.constant.condition.when.reached" :
"dataflow.message.constant.condition", evaluatesToTrue ? 1 : 0);
reporter.registerProblem(psiAnchor, message, fixes.toArray(LocalQuickFix.EMPTY_ARRAY));
}
protected @Nullable LocalQuickFix createExplainFix(PsiExpression anchor, TrackingRunner.DfaProblemType problemType) {
return null;
}
private static boolean isCoveredBySurroundingFix(PsiElement anchor, boolean evaluatesToTrue) {
PsiElement parent = PsiUtil.skipParenthesizedExprUp(anchor.getParent());
if (parent instanceof PsiPolyadicExpression) {
IElementType tokenType = ((PsiPolyadicExpression)parent).getOperationTokenType();
return tokenType.equals(JavaTokenType.ANDAND) && !evaluatesToTrue ||
tokenType.equals(JavaTokenType.OROR) && evaluatesToTrue;
}
return parent instanceof PsiExpression && BoolUtils.isNegation((PsiExpression)parent);
}
@Contract("null -> false")
private static boolean shouldBeSuppressed(PsiElement anchor) {
if (!(anchor instanceof PsiExpression)) return false;
// Don't report System.out.println(b = false) or doSomething((Type)null)
if (anchor instanceof PsiAssignmentExpression ||
(anchor instanceof PsiTypeCastExpression && !(((PsiTypeCastExpression)anchor).getType() instanceof PsiPrimitiveType))) {
return true;
}
// For conditional the root cause (constant condition or both branches constant) should be already reported for branches
if (anchor instanceof PsiConditionalExpression) return true;
PsiExpression expression = (PsiExpression)anchor;
if (expression instanceof PsiReferenceExpression) {
PsiReferenceExpression ref = (PsiReferenceExpression)expression;
if ("TRUE".equals(ref.getReferenceName()) || "FALSE".equals(ref.getReferenceName())) {
PsiElement target = ref.resolve();
if (target instanceof PsiField) {
PsiClass containingClass = ((PsiField)target).getContainingClass();
if (containingClass != null && CommonClassNames.JAVA_LANG_BOOLEAN.equals(containingClass.getQualifiedName())) return true;
}
}
}
if (expression instanceof PsiBinaryExpression) {
PsiExpression lOperand = ((PsiBinaryExpression)expression).getLOperand();
PsiExpression rOperand = ((PsiBinaryExpression)expression).getROperand();
IElementType tokenType = ((PsiBinaryExpression)expression).getOperationTokenType();
// Suppress on type mismatch compilation errors
if (rOperand == null) return true;
PsiType lType = lOperand.getType();
PsiType rType = rOperand.getType();
if (lType == null || rType == null) return true;
if (!TypeConversionUtil.isBinaryOperatorApplicable(tokenType, lType, rType, false)) return true;
}
if (expression instanceof PsiInstanceOfExpression) {
PsiType type = ((PsiInstanceOfExpression)expression).getOperand().getType();
private static boolean shouldBeSuppressed(@NotNull PsiExpression anchor) {
if (anchor instanceof PsiInstanceOfExpression) {
PsiType type = ((PsiInstanceOfExpression)anchor).getOperand().getType();
if (type == null || !TypeConstraints.instanceOf(type).isResolved()) return true;
PsiPattern pattern = ((PsiInstanceOfExpression)expression).getPattern();
PsiPattern pattern = ((PsiInstanceOfExpression)anchor).getPattern();
if (pattern instanceof PsiTypeTestPattern && ((PsiTypeTestPattern)pattern).getPatternVariable() != null) {
PsiTypeElement checkType = ((PsiTypeTestPattern)pattern).getCheckType();
if (checkType != null && checkType.getType().isAssignableFrom(type)) {
// Reported as compilation error
return true;
}
// Reported as compilation error
return checkType != null && checkType.getType().isAssignableFrom(type);
}
}
PsiElement parent = PsiUtil.skipParenthesizedExprUp(expression.getParent());
// Don't report "x" in "x == null" as will be anyway reported as "always true"
if (parent instanceof PsiBinaryExpression && ExpressionUtils.getValueComparedWithNull((PsiBinaryExpression)parent) != null) return true;
// Dereference of null will be covered by other warning
if (ExpressionUtils.isVoidContext(expression) || isDereferenceContext(expression)) return true;
// We assume all Void variables as null because you cannot instantiate it without dirty hacks
// However reporting them as "always null" looks redundant (dereferences or comparisons will be reported though).
if (TypeUtils.typeEquals(CommonClassNames.JAVA_LANG_VOID, expression.getType())) return true;
if (isFlagCheck(anchor)) return true;
if (!isCondition(expression) && expression instanceof PsiMethodCallExpression) {
List<? extends MethodContract> contracts = JavaMethodContractUtil.getMethodCallContracts((PsiCallExpression)expression);
ContractReturnValue value = JavaMethodContractUtil.getNonFailingReturnValue(contracts);
if (value != null) return true;
if (!(parent instanceof PsiAssignmentExpression) && !(parent instanceof PsiVariable) &&
!(parent instanceof PsiReturnStatement)) {
PsiMethod method = ((PsiMethodCallExpression)expression).resolveMethod();
if (method == null || !JavaMethodContractUtil.isPure(method)) return true;
}
}
while (expression != null && BoolUtils.isNegation(expression)) {
expression = BoolUtils.getNegated(expression);
}
if (expression == null) return false;
if (!isCondition(expression) && expression instanceof PsiReferenceExpression) {
PsiVariable variable = tryCast(((PsiReferenceExpression)expression).resolve(), PsiVariable.class);
if (variable instanceof PsiField &&
variable.hasModifierProperty(PsiModifier.STATIC) &&
ExpressionUtils.isNullLiteral(PsiFieldImpl.getDetachedInitializer(variable))) {
return true;
}
if (variable instanceof PsiLocalVariable && variable.hasInitializer()) {
boolean effectivelyFinal = variable.hasModifierProperty(PsiModifier.FINAL) ||
!VariableAccessUtils.variableIsAssigned(variable, PsiUtil.getVariableCodeBlock(variable, null));
return effectivelyFinal && PsiUtil.isConstantExpression(variable.getInitializer());
}
return false;
}
// Avoid double reporting
return expression instanceof PsiMethodCallExpression && EqualsWithItselfInspection.isEqualsWithItself((PsiMethodCallExpression)expression) ||
expression instanceof PsiBinaryExpression && ComparisonToNaNInspection.extractNaNFromComparison((PsiBinaryExpression)expression) != null;
}
private static boolean isDereferenceContext(PsiExpression ref) {
PsiElement parent = PsiUtil.skipParenthesizedExprUp(ref.getParent());
return parent instanceof PsiReferenceExpression || parent instanceof PsiArrayAccessExpression
|| parent instanceof PsiSwitchStatement || parent instanceof PsiSynchronizedStatement;
}
private static LocalQuickFix createReplaceWithNullCheckFix(PsiElement psiAnchor, boolean evaluatesToTrue) {
if (evaluatesToTrue) return null;
if (!(psiAnchor instanceof PsiMethodCallExpression)) return null;
final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)psiAnchor;
if (!MethodCallUtils.isEqualsCall(methodCallExpression)) return null;
PsiExpression arg = ArrayUtil.getFirstElement(methodCallExpression.getArgumentList().getExpressions());
if (!ExpressionUtils.isNullLiteral(arg)) return null;
PsiElement parent = PsiUtil.skipParenthesizedExprUp(psiAnchor.getParent());
return EqualsToEqualityFix.buildFix(methodCallExpression, parent instanceof PsiExpression && BoolUtils.isNegation((PsiExpression)parent));
}
protected LocalQuickFix[] createConditionalAssignmentFixes(boolean evaluatesToTrue, PsiAssignmentExpression parent, final boolean onTheFly) {
return LocalQuickFix.EMPTY_ARRAY;
return false;
}
private static @Nullable PsiMethod getScopeMethod(PsiElement block) {
@@ -1071,7 +760,6 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec
private void reportNullableReturns(ProblemReporter reporter,
List<NullabilityProblem<?>> problems,
Map<PsiExpression, ConstantResult> expressions,
@NotNull PsiElement block) {
final PsiMethod method = getScopeMethod(block);
if (method == null) return;
@@ -1099,7 +787,7 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec
final PsiExpression anchor = problem.getAnchor();
PsiExpression expr = problem.getDereferencedExpression();
boolean exactlyNull = problem.isAlwaysNull(expressions);
boolean exactlyNull = problem.isAlwaysNull(IGNORE_ASSERT_STATEMENTS);
if (!REPORT_UNSOUND_WARNINGS && !exactlyNull) continue;
if (nullability == Nullability.NOT_NULL) {
String presentable = NullableStuffInspectionBase.getPresentableAnnoName(anno);
@@ -1123,198 +811,6 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec
}
}
private static boolean isAssertionEffectively(@NotNull PsiExpression anchor, ConstantResult result) {
Object value = result.value();
if (value instanceof Boolean) {
return isAssertionEffectively(anchor, (Boolean)value);
}
if (value != null) return false;
return isAssertCallArgument(anchor, ContractValue.nullValue());
}
/**
* @param anchor boolean expression
* @param expectedValue the expected result of boolean expression
* @return true if this boolean expression is effectively an assertion (code throws if its value is not equal to expectedValue)
*/
public static boolean isAssertionEffectively(@NotNull PsiExpression anchor, boolean expectedValue) {
PsiElement parent;
while (true) {
parent = anchor.getParent();
if (parent instanceof PsiExpression parentExpr && BoolUtils.isNegation(parentExpr)) {
expectedValue = !expectedValue;
anchor = parentExpr;
continue;
}
if (parent instanceof PsiParenthesizedExpression parenthesized) {
anchor = parenthesized;
continue;
}
if (parent instanceof PsiPolyadicExpression polyadic) {
IElementType tokenType = polyadic.getOperationTokenType();
if (tokenType.equals(JavaTokenType.ANDAND) || tokenType.equals(JavaTokenType.OROR)) {
// always true operand makes always true OR-chain and does not affect the result of AND-chain
// Note that in `assert unknownExpression && trueExpression;` the trueExpression should not be reported
// because this assertion is essentially the shortened `assert unknownExpression; assert trueExpression;`
// which is not reported.
boolean causesShortCircuit = (tokenType.equals(JavaTokenType.OROR) == expectedValue) &&
ArrayUtil.getLastElement(polyadic.getOperands()) != anchor;
if (!causesShortCircuit) {
// We still report `assert trueExpression || unknownExpression`, because here `unknownExpression` is never checked
// which is probably not intended.
anchor = polyadic;
continue;
}
}
}
break;
}
if (parent instanceof PsiAssertStatement) {
return expectedValue;
}
if (parent instanceof PsiIfStatement ifStatement && anchor == ifStatement.getCondition()) {
PsiStatement thenBranch = ControlFlowUtils.stripBraces(ifStatement.getThenBranch());
if (thenBranch instanceof PsiThrowStatement) {
return !expectedValue;
}
}
return isAssertCallArgument(anchor, ContractValue.booleanValue(expectedValue));
}
private static boolean isAssertCallArgument(@NotNull PsiElement anchor, @NotNull ContractValue wantedConstraint) {
PsiElement parent = PsiUtil.skipParenthesizedExprUp(anchor.getParent());
if (parent instanceof PsiExpressionList) {
int index = ArrayUtil.indexOf(((PsiExpressionList)parent).getExpressions(), anchor);
if (index >= 0) {
PsiMethodCallExpression call = tryCast(parent.getParent(), PsiMethodCallExpression.class);
if (call != null) {
MethodContract contract = ContainerUtil.getOnlyItem(JavaMethodContractUtil.getMethodCallContracts(call));
if (contract != null && contract.getReturnValue().isFail()) {
ContractValue condition = ContainerUtil.getOnlyItem(contract.getConditions());
if (condition != null) {
return condition.getArgumentComparedTo(wantedConstraint, false).orElse(-1) == index;
}
}
}
}
}
return false;
}
private static boolean isAtRHSOfBooleanAnd(PsiElement expr) {
PsiElement cur = expr;
while (cur != null && !(cur instanceof PsiMember)) {
PsiElement parent = cur.getParent();
if (parent instanceof PsiBinaryExpression && cur == ((PsiBinaryExpression)parent).getROperand()) {
return true;
}
cur = parent;
}
return false;
}
private static boolean isFlagCheck(PsiElement element) {
PsiElement scope = PsiTreeUtil.getParentOfType(element, PsiStatement.class, PsiVariable.class);
PsiExpression topExpression = scope instanceof PsiIfStatement ? ((PsiIfStatement)scope).getCondition() :
scope instanceof PsiVariable ? ((PsiVariable)scope).getInitializer() :
null;
if (!PsiTreeUtil.isAncestor(topExpression, element, false)) return false;
return StreamEx.<PsiElement>ofTree(topExpression, e -> StreamEx.of(e.getChildren()))
.anyMatch(DataFlowInspectionBase::isCompileTimeFlagCheck);
}
private static boolean isCompileTimeFlagCheck(PsiElement element) {
if(element instanceof PsiBinaryExpression) {
PsiBinaryExpression binOp = (PsiBinaryExpression)element;
if(ComparisonUtils.isComparisonOperation(binOp.getOperationTokenType())) {
PsiExpression comparedWith = null;
if(ExpressionUtils.isLiteral(binOp.getROperand())) {
comparedWith = binOp.getLOperand();
} else if(ExpressionUtils.isLiteral(binOp.getLOperand())) {
comparedWith = binOp.getROperand();
}
comparedWith = PsiUtil.skipParenthesizedExprDown(comparedWith);
if (isConstantOfType(comparedWith, PsiType.INT, PsiType.LONG)) {
// like "if(DEBUG_LEVEL > 2)"
return true;
}
if(comparedWith instanceof PsiBinaryExpression) {
PsiBinaryExpression subOp = (PsiBinaryExpression)comparedWith;
if(subOp.getOperationTokenType().equals(JavaTokenType.AND)) {
PsiExpression left = PsiUtil.skipParenthesizedExprDown(subOp.getLOperand());
PsiExpression right = PsiUtil.skipParenthesizedExprDown(subOp.getROperand());
if(isConstantOfType(left, PsiType.INT, PsiType.LONG) ||
isConstantOfType(right, PsiType.INT, PsiType.LONG)) {
// like "if((FLAGS & SOME_FLAG) != 0)"
return true;
}
}
}
}
}
// like "if(DEBUG)"
return isConstantOfType(element, PsiType.BOOLEAN);
}
private static boolean isConstantOfType(PsiElement element, PsiPrimitiveType... types) {
PsiElement resolved = element instanceof PsiReferenceExpression ? ((PsiReferenceExpression)element).resolve() : null;
if (!(resolved instanceof PsiField)) return false;
PsiField field = (PsiField)resolved;
PsiModifierList modifierList = field.getModifierList();
if (modifierList == null ||
!modifierList.hasModifierProperty(PsiModifier.STATIC) ||
!modifierList.hasModifierProperty(PsiModifier.FINAL)) {
return false;
}
if (!ArrayUtil.contains(field.getType(), types)) return false;
return field.hasInitializer() && PsiUtil.isConstantExpression(field.getInitializer());
}
private @Nullable LocalQuickFix createSimplifyBooleanExpressionFix(PsiElement element, final boolean value) {
LocalQuickFixOnPsiElement fix = createSimplifyBooleanFix(element, value);
if (fix == null) return null;
final String text = fix.getText();
return new LocalQuickFix() {
@Override
public @NotNull String getName() {
return text;
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
final PsiElement psiElement = descriptor.getPsiElement();
if (psiElement == null) return;
final LocalQuickFixOnPsiElement fix = createSimplifyBooleanFix(psiElement, value);
if (fix == null) return;
try {
LOG.assertTrue(psiElement.isValid());
fix.applyFix();
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
}
@Override
public @NotNull String getFamilyName() {
return JavaAnalysisBundle.message("inspection.data.flow.simplify.boolean.expression.quickfix");
}
};
}
protected static @NotNull LocalQuickFix createSimplifyToAssignmentFix() {
return new SimplifyToAssignmentFix();
}
protected LocalQuickFixOnPsiElement createSimplifyBooleanFix(PsiElement element, boolean value) {
return null;
}
@Override
public @NotNull String getGroupDisplayName() {
return InspectionsBundle.message("group.names.probable.bugs");
@@ -1325,44 +821,6 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec
return SHORT_NAME;
}
protected enum ConstantResult {
TRUE, FALSE, NULL, ZERO, UNKNOWN;
@Override
public @NotNull String toString() {
return this == ZERO ? "0" : StringUtil.toLowerCase(name());
}
public Object value() {
switch (this) {
case TRUE:
return Boolean.TRUE;
case FALSE:
return Boolean.FALSE;
case ZERO:
return 0;
case NULL:
return null;
default:
throw new UnsupportedOperationException();
}
}
static @NotNull ConstantResult fromDfType(@NotNull DfType dfType) {
if (dfType == DfTypes.NULL) return NULL;
if (dfType == DfTypes.TRUE) return TRUE;
if (dfType == DfTypes.FALSE) return FALSE;
if (dfType.isConst(0) || dfType.isConst(0L)) return ZERO;
return UNKNOWN;
}
static @NotNull ConstantResult mergeValue(@Nullable ConstantResult state, @NotNull DfaMemoryState memState, @Nullable DfaValue value) {
if (state == UNKNOWN || value == null) return UNKNOWN;
ConstantResult nextState = fromDfType(DfaUtil.getUnboxedDfType(memState, value));
return state == null || state == nextState ? nextState : UNKNOWN;
}
}
/**
* {@link ProblemsHolder} wrapper to avoid reporting two problems on the same anchor
*/
@@ -1382,21 +840,6 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec
}
}
void registerProblem(PsiElement element, @InspectionMessage String message, ProblemHighlightType type, LocalQuickFix... fixes) {
if (register(element)) {
myHolder.registerProblem(element, message, type, fixes);
}
}
void registerProblem(PsiElement element, TextRange range, @InspectionMessage String message, LocalQuickFix... fixes) {
if (range == null) {
registerProblem(element, message, fixes);
}
else {
myHolder.registerProblem(element, range, message, fixes);
}
}
private boolean register(PsiElement element) {
// Suppress reporting for inlined simple methods
if (!PsiTreeUtil.isAncestor(myScope, element, false)) return false;

View File

@@ -1,7 +1,6 @@
// Copyright 2000-2019 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.dataFlow;
import com.intellij.codeInspection.dataFlow.DataFlowInspectionBase.ConstantResult;
import com.intellij.codeInspection.dataFlow.java.JavaDfaListener;
import com.intellij.codeInspection.dataFlow.java.JavaDfaValueFactory;
import com.intellij.codeInspection.dataFlow.java.anchor.JavaExpressionAnchor;
@@ -52,7 +51,6 @@ final class DataFlowInstructionVisitor implements JavaDfaListener {
private final Map<PsiTypeCastExpression, StateInfo> myClassCastProblems = new HashMap<>();
private final Map<PsiTypeCastExpression, TypeConstraint> myRealOperandTypes = new HashMap<>();
private final Map<ContractFailureProblem, Boolean> myFailingCalls = new HashMap<>();
private final Map<DfaAnchor, ConstantResult> myConstantExpressions = new HashMap<>();
private final Map<PsiElement, ThreeState> myOfNullableCalls = new HashMap<>();
private final Map<PsiAssignmentExpression, Pair<PsiType, PsiType>> myArrayStoreProblems = new HashMap<>();
private final Map<PsiArrayAccessExpression, ThreeState> myOutOfBoundsArrayAccesses = new HashMap<>();
@@ -65,6 +63,7 @@ final class DataFlowInstructionVisitor implements JavaDfaListener {
private boolean myAlwaysReturnsNotNull = true;
private final List<DfaMemoryState> myEndOfInitializerStates = new ArrayList<>();
private final Set<DfaAnchor> myPotentiallyRedundantInstanceOf = new HashSet<>();
private final Map<DfaAnchor, ThreeState> myConstantInstanceOf = new HashMap<>();
private final boolean myStrictMode;
private static final CallMatcher USELESS_SAME_ARGUMENTS = CallMatcher.anyOf(
@@ -174,15 +173,6 @@ final class DataFlowInstructionVisitor implements JavaDfaListener {
return myOfNullableCalls;
}
Map<PsiExpression, ConstantResult> getConstantExpressions() {
return EntryStream.of(myConstantExpressions).selectKeys(JavaExpressionAnchor.class)
.mapKeys(JavaExpressionAnchor::getExpression).toMap();
}
Map<DfaAnchor, ConstantResult> getConstantExpressionChunks() {
return myConstantExpressions;
}
Map<PsiCaseLabelElement, ThreeState> getSwitchLabelsReachability() {
return mySwitchLabelsReachability;
}
@@ -218,7 +208,7 @@ final class DataFlowInstructionVisitor implements JavaDfaListener {
}
StreamEx<DfaAnchor> redundantInstanceOfs() {
return StreamEx.of(myPotentiallyRedundantInstanceOf).filter(anchor -> myConstantExpressions.get(anchor) == ConstantResult.UNKNOWN);
return StreamEx.of(myPotentiallyRedundantInstanceOf).filter(anchor -> myConstantInstanceOf.get(anchor) == ThreeState.UNSURE);
}
@Override
@@ -242,15 +232,22 @@ final class DataFlowInstructionVisitor implements JavaDfaListener {
}
if (anchor instanceof JavaSwitchLabelTakenAnchor) {
DfType type = state.getDfType(value);
mySwitchLabelsReachability.merge(((JavaSwitchLabelTakenAnchor)anchor).getLabelElement(),
type.equals(DfTypes.TRUE) ? ThreeState.YES :
type.equals(DfTypes.FALSE) ? ThreeState.NO : ThreeState.UNSURE, ThreeState::merge);
mySwitchLabelsReachability.merge(((JavaSwitchLabelTakenAnchor)anchor).getLabelElement(), fromDfType(type), ThreeState::merge);
return;
}
if (myPotentiallyRedundantInstanceOf.contains(anchor) && isUsefulInstanceof(args, value, state)) {
myPotentiallyRedundantInstanceOf.remove(anchor);
if (myPotentiallyRedundantInstanceOf.contains(anchor)) {
if (isUsefulInstanceof(args, value, state)) {
myPotentiallyRedundantInstanceOf.remove(anchor);
} else {
myConstantInstanceOf.merge(anchor, fromDfType(state.getDfType(value)), ThreeState::merge);
}
}
myConstantExpressions.compute(anchor, (c, curState) -> ConstantResult.mergeValue(curState, state, value));
}
@NotNull
private static ThreeState fromDfType(DfType type) {
return type.equals(DfTypes.TRUE) ? ThreeState.YES :
type.equals(DfTypes.FALSE) ? ThreeState.NO : ThreeState.UNSURE;
}
private static boolean isUsefulInstanceof(@NotNull DfaValue @NotNull [] args,
@@ -436,24 +433,14 @@ final class DataFlowInstructionVisitor implements JavaDfaListener {
}
}
static class ArgResultEquality {
final boolean argsEqual;
final boolean firstArgEqualToResult;
final boolean secondArgEqualToResult;
ArgResultEquality(boolean argsEqual, boolean firstArgEqualToResult, boolean secondArgEqualToResult) {
this.argsEqual = argsEqual;
this.firstArgEqualToResult = firstArgEqualToResult;
this.secondArgEqualToResult = secondArgEqualToResult;
}
record ArgResultEquality(boolean argsEqual, boolean firstArgEqualToResult, boolean secondArgEqualToResult) {
ArgResultEquality merge(ArgResultEquality other) {
return new ArgResultEquality(argsEqual && other.argsEqual, firstArgEqualToResult && other.firstArgEqualToResult,
secondArgEqualToResult && other.secondArgEqualToResult);
}
return new ArgResultEquality(argsEqual && other.argsEqual, firstArgEqualToResult && other.firstArgEqualToResult,
secondArgEqualToResult && other.secondArgEqualToResult);
}
boolean hasEquality() {
return argsEqual || firstArgEqualToResult || secondArgEqualToResult;
boolean hasEquality() {
return argsEqual || firstArgEqualToResult || secondArgEqualToResult;
}
}
}
}

View File

@@ -43,7 +43,9 @@ import com.intellij.util.containers.JBIterable;
import com.intellij.util.containers.MultiMap;
import com.intellij.util.containers.Stack;
import com.siyeh.ig.callMatcher.CallMatcher;
import com.siyeh.ig.psiutils.BoolUtils;
import com.siyeh.ig.psiutils.ClassUtils;
import com.siyeh.ig.psiutils.ControlFlowUtils;
import one.util.streamex.StreamEx;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -51,6 +53,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.*;
import static com.intellij.psi.CommonClassNames.*;
import static com.intellij.util.ObjectUtils.tryCast;
import static com.siyeh.ig.callMatcher.CallMatcher.staticCall;
public final class DfaPsiUtil {
@@ -698,4 +701,78 @@ public final class DfaPsiUtil {
}
return value.toString();
}
/**
* @param anchor boolean expression
* @param expectedValue the expected result of boolean expression
* @return true if this boolean expression is effectively an assertion (code throws if its value is not equal to expectedValue)
*/
public static boolean isAssertionEffectively(@NotNull PsiExpression anchor, boolean expectedValue) {
PsiElement parent;
while (true) {
parent = anchor.getParent();
if (parent instanceof PsiExpression parentExpr && BoolUtils.isNegation(parentExpr)) {
expectedValue = !expectedValue;
anchor = parentExpr;
continue;
}
if (parent instanceof PsiParenthesizedExpression parenthesized) {
anchor = parenthesized;
continue;
}
if (parent instanceof PsiPolyadicExpression polyadic) {
IElementType tokenType = polyadic.getOperationTokenType();
if (tokenType.equals(JavaTokenType.ANDAND) || tokenType.equals(JavaTokenType.OROR)) {
// always true operand makes always true OR-chain and does not affect the result of AND-chain
// Note that in `assert unknownExpression && trueExpression;` the trueExpression should not be reported
// because this assertion is essentially the shortened `assert unknownExpression; assert trueExpression;`
// which is not reported.
boolean causesShortCircuit = (tokenType.equals(JavaTokenType.OROR) == expectedValue) &&
ArrayUtil.getLastElement(polyadic.getOperands()) != anchor;
if (!causesShortCircuit) {
// We still report `assert trueExpression || unknownExpression`, because here `unknownExpression` is never checked
// which is probably not intended.
anchor = polyadic;
continue;
}
}
}
break;
}
if (parent instanceof PsiAssertStatement) {
return expectedValue;
}
if (parent instanceof PsiIfStatement ifStatement && anchor == ifStatement.getCondition()) {
PsiStatement thenBranch = ControlFlowUtils.stripBraces(ifStatement.getThenBranch());
if (thenBranch instanceof PsiThrowStatement) {
return !expectedValue;
}
}
return isAssertCallArgument(anchor, ContractValue.booleanValue(expectedValue));
}
/**
* @param anchor expression
* @param wantedConstraint expected contract value
* @return true if this expression is an argument for an assertion call that ensures that the expression is equal to the expected value
*/
public static boolean isAssertCallArgument(@NotNull PsiElement anchor, @NotNull ContractValue wantedConstraint) {
PsiElement parent = PsiUtil.skipParenthesizedExprUp(anchor.getParent());
if (parent instanceof PsiExpressionList) {
int index = ArrayUtil.indexOf(((PsiExpressionList)parent).getExpressions(), anchor);
if (index >= 0) {
PsiMethodCallExpression call = tryCast(parent.getParent(), PsiMethodCallExpression.class);
if (call != null) {
MethodContract contract = ContainerUtil.getOnlyItem(JavaMethodContractUtil.getMethodCallContracts(call));
if (contract != null && contract.getReturnValue().isFail()) {
ContractValue condition = ContainerUtil.getOnlyItem(contract.getConditions());
if (condition != null) {
return condition.getArgumentComparedTo(wantedConstraint, false).orElse(-1) == index;
}
}
}
}
}
return false;
}
}

View File

@@ -251,15 +251,19 @@ public final class DfaUtil {
* @return a dataflow context; null if no applicable context found.
*/
static @Nullable PsiElement getDataflowContext(PsiExpression expression) {
PsiMember member = PsiTreeUtil.getParentOfType(expression, PsiMember.class);
while (member instanceof PsiAnonymousClass && PsiTreeUtil.isAncestor(((PsiAnonymousClass)member).getArgumentList(), expression, true)) {
member = PsiTreeUtil.getParentOfType(member, PsiMember.class);
PsiElement element = expression;
while (true) {
element = element.getParent();
if (element == null || element instanceof PsiAnnotation) return null;
if (element instanceof PsiMethod method && !method.isConstructor()) {
PsiClass containingClass = method.getContainingClass();
if (containingClass != null &&
(!PsiUtil.isLocalOrAnonymousClass(containingClass) || containingClass instanceof PsiEnumConstantInitializer)) {
return method.getBody();
}
}
if (element instanceof PsiClass psiClass && !PsiUtil.isLocalOrAnonymousClass(psiClass)) return psiClass;
}
if (member instanceof PsiField || member instanceof PsiClassInitializer) return member.getContainingClass();
if (member instanceof PsiMethod) {
return ((PsiMethod)member).isConstructor() ? member.getContainingClass() : ((PsiMethod)member).getBody();
}
return null;
}
/**

View File

@@ -5,6 +5,7 @@ import com.intellij.codeInsight.Nullability;
import com.intellij.codeInspection.dataFlow.java.CFGBuilder;
import com.intellij.codeInspection.dataFlow.java.ControlFlowAnalyzer;
import com.intellij.codeInspection.dataFlow.jvm.problems.JvmDfaProblem;
import com.intellij.codeInspection.dataFlow.types.DfTypes;
import com.intellij.codeInspection.util.InspectionMessage;
import com.intellij.java.analysis.JavaAnalysisBundle;
import com.intellij.pom.java.LanguageLevel;
@@ -579,19 +580,19 @@ public final class NullabilityProblemKind<T extends PsiElement> {
return myFromUnknown;
}
public boolean isAlwaysNull(@NotNull Map<PsiExpression, DataFlowInspectionBase.ConstantResult> expressions) {
public boolean isAlwaysNull(boolean ignoreAssertions) {
PsiExpression expression = PsiUtil.skipParenthesizedExprDown(getDereferencedExpression());
return expression != null &&
(ExpressionUtils.isNullLiteral(expression) || expressions.get(expression) == DataFlowInspectionBase.ConstantResult.NULL);
(ExpressionUtils.isNullLiteral(expression) || CommonDataflow.getDfType(expression, ignoreAssertions) == DfTypes.NULL);
}
@NotNull
public @InspectionMessage String getMessage(@NotNull Map<PsiExpression, DataFlowInspectionBase.ConstantResult> expressions) {
public @InspectionMessage String getMessage(boolean ignoreAssertions) {
if (myKind.myAlwaysNullMessage == null || myKind.myNormalMessage == null) {
throw new IllegalStateException("This problem kind has no message associated: " + myKind);
}
String suffix = myFromUnknown ? JavaAnalysisBundle.message("dataflow.message.unknown.nullability") : "";
Supplier<@Nls String> msg = isAlwaysNull(expressions) ? myKind.myAlwaysNullMessage : myKind.myNormalMessage;
Supplier<@Nls String> msg = isAlwaysNull(ignoreAssertions) ? myKind.myAlwaysNullMessage : myKind.myNormalMessage;
return msg.get() + suffix;
}

View File

@@ -69,13 +69,6 @@ public interface JavaDfaListener extends DfaListener {
!PsiTreeUtil.isAncestor(((PsiConditionalExpression)parent).getCondition(), anchor, false)) {
callBeforeExpressionPush(value, expression, state, (PsiConditionalExpression)parent);
}
else if (parent instanceof PsiPolyadicExpression) {
PsiPolyadicExpression polyadic = (PsiPolyadicExpression)parent;
if ((polyadic.getOperationTokenType().equals(JavaTokenType.ANDAND) || polyadic.getOperationTokenType().equals(JavaTokenType.OROR)) &&
PsiTreeUtil.isAncestor(ArrayUtil.getLastElement(polyadic.getOperands()), anchor, false)) {
callBeforeExpressionPush(value, expression, state, polyadic);
}
}
}
/**

View File

@@ -0,0 +1,585 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInspection.dataFlow;
import com.intellij.codeInsight.daemon.impl.quickfix.SimplifyBooleanExpressionFix;
import com.intellij.codeInspection.*;
import com.intellij.codeInspection.dataFlow.CommonDataflow.DataflowResult;
import com.intellij.codeInspection.dataFlow.fix.FindDfaProblemCauseFix;
import com.intellij.codeInspection.dataFlow.fix.ReplaceWithConstantValueFix;
import com.intellij.codeInspection.dataFlow.fix.SimplifyToAssignmentFix;
import com.intellij.codeInspection.dataFlow.java.anchor.JavaDfaAnchor;
import com.intellij.codeInspection.dataFlow.java.anchor.JavaExpressionAnchor;
import com.intellij.codeInspection.dataFlow.java.anchor.JavaMethodReferenceReturnAnchor;
import com.intellij.codeInspection.dataFlow.java.anchor.JavaPolyadicPartAnchor;
import com.intellij.codeInspection.dataFlow.jvm.SpecialField;
import com.intellij.codeInspection.dataFlow.types.DfReferenceType;
import com.intellij.codeInspection.dataFlow.types.DfType;
import com.intellij.codeInspection.dataFlow.types.DfTypes;
import com.intellij.codeInspection.ui.InspectionOptionsPanel;
import com.intellij.java.analysis.JavaAnalysisBundle;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.PsiFieldImpl;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.SmartList;
import com.intellij.util.containers.ContainerUtil;
import com.siyeh.ig.bugs.EqualsWithItselfInspection;
import com.siyeh.ig.fixes.EqualsToEqualityFix;
import com.siyeh.ig.numeric.ComparisonToNaNInspection;
import com.siyeh.ig.psiutils.*;
import one.util.streamex.StreamEx;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.ArrayList;
import java.util.List;
import static com.intellij.java.JavaBundle.message;
import static com.intellij.util.ObjectUtils.tryCast;
public class ConstantValueInspection extends AbstractBaseJavaLocalInspectionTool {
private static final Logger LOG = Logger.getInstance(ConstantValueInspection.class);
public boolean DONT_REPORT_TRUE_ASSERT_STATEMENTS;
public boolean IGNORE_ASSERT_STATEMENTS;
public boolean REPORT_CONSTANT_REFERENCE_VALUES = true;
@Override
public @Nullable JComponent createOptionsPanel() {
InspectionOptionsPanel panel = new InspectionOptionsPanel();
panel.addCheckbox(message("inspection.data.flow.true.asserts.option"),
"DONT_REPORT_TRUE_ASSERT_STATEMENTS");
panel.addCheckbox(message("inspection.data.flow.ignore.assert.statements"),
"IGNORE_ASSERT_STATEMENTS");
panel.addCheckbox(message("inspection.data.flow.warn.when.reading.a.value.guaranteed.to.be.constant"),
"REPORT_CONSTANT_REFERENCE_VALUES");
return panel;
}
@Override
public @NotNull PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
return new JavaElementVisitor() {
@Override
public void visitReferenceExpression(@NotNull PsiReferenceExpression expression) {
visitExpression(expression);
}
@Override
public void visitExpression(@NotNull PsiExpression expression) {
if (expression instanceof PsiLiteralExpression) return;
DataflowResult dfr = CommonDataflow.getDataflowResult(expression);
if (dfr == null) return;
JavaDfaAnchor anchor = expression instanceof PsiMethodReferenceExpression methodRef ?
new JavaMethodReferenceReturnAnchor(methodRef) :
new JavaExpressionAnchor(expression);
processAnchor(dfr, anchor, holder);
if (expression instanceof PsiPolyadicExpression polyadic) {
PsiExpression[] operands = polyadic.getOperands();
for (int i = 1; i < operands.length - 1; i++) {
processAnchor(dfr, new JavaPolyadicPartAnchor(polyadic, i), holder);
}
}
}
@Override
public void visitIfStatement(@NotNull PsiIfStatement statement) {
PsiExpression condition = PsiUtil.skipParenthesizedExprDown(statement.getCondition());
if (BoolUtils.isBooleanLiteral(condition)) {
LocalQuickFix fix = createSimplifyBooleanExpressionFix(condition, condition.textMatches(PsiKeyword.TRUE));
holder.registerProblem(condition, JavaAnalysisBundle
.message("dataflow.message.constant.no.ref", condition.textMatches(PsiKeyword.TRUE) ? 1 : 0), fix);
}
}
@Override
public void visitDoWhileStatement(@NotNull PsiDoWhileStatement statement) {
PsiExpression condition = PsiUtil.skipParenthesizedExprDown(statement.getCondition());
if (condition != null && condition.textMatches(PsiKeyword.FALSE)) {
holder.registerProblem(condition, JavaAnalysisBundle.message("dataflow.message.constant.no.ref", 0),
createSimplifyBooleanExpressionFix(condition, false));
}
}
};
}
private void processAnchor(@NotNull DataflowResult dfr,
@NotNull JavaDfaAnchor anchor,
@NotNull ProblemsHolder holder) {
DfType dfType;
if (IGNORE_ASSERT_STATEMENTS) {
dfType = dfr.getDfTypeNoAssertions(anchor);
} else {
dfType = dfr.getDfType(anchor);
}
processAnchor(dfType, anchor, holder);
}
private void processAnchor(@NotNull DfType dfType, @NotNull JavaDfaAnchor anchor, @NotNull ProblemsHolder reporter) {
ConstantResult result = ConstantResult.fromDfType(dfType);
if (result == ConstantResult.UNKNOWN && dfType instanceof DfReferenceType refType && refType.getSpecialField() == SpecialField.UNBOX) {
result = ConstantResult.fromDfType(refType.getSpecialFieldType());
}
if (result == ConstantResult.UNKNOWN) return;
Object value = result.value();
if (anchor instanceof JavaPolyadicPartAnchor) {
if (value instanceof Boolean) {
// report rare cases like a == b == c where "a == b" part is constant
String message = JavaAnalysisBundle.message("dataflow.message.constant.condition",
((Boolean)value).booleanValue() ? 1 : 0);
reporter.registerProblem(((JavaPolyadicPartAnchor)anchor).getExpression(),
((JavaPolyadicPartAnchor)anchor).getTextRange(), message);
// do not add to reported anchors if only part of expression was reported
}
}
else if (anchor instanceof JavaExpressionAnchor) {
PsiExpression expression = ((JavaExpressionAnchor)anchor).getExpression();
if (isCondition(expression)) {
if (value instanceof Boolean) {
reportConstantBoolean(reporter, expression, (Boolean)value);
}
}
else {
reportConstantReferenceValue(reporter, expression, result);
}
}
else if (anchor instanceof JavaMethodReferenceReturnAnchor methodRefAnchor) {
PsiMethodReferenceExpression methodRef = methodRefAnchor.getMethodReferenceExpression();
PsiMethod method = tryCast(methodRef.resolve(), PsiMethod.class);
if (method != null && JavaMethodContractUtil.isPure(method)) {
List<StandardMethodContract> contracts = JavaMethodContractUtil.getMethodContracts(method);
if (contracts.isEmpty() || !contracts.get(0).isTrivial()) {
reporter.registerProblem(methodRef, JavaAnalysisBundle.message("dataflow.message.constant.method.reference", value),
new ReplaceWithTrivialLambdaFix(value));
}
}
}
}
private static boolean shouldReportZero(PsiExpression ref) {
if (ref instanceof PsiPolyadicExpression) {
if (PsiUtil.isConstantExpression(ref)) return false;
PsiPolyadicExpression polyadic = (PsiPolyadicExpression)ref;
IElementType tokenType = polyadic.getOperationTokenType();
if (tokenType.equals(JavaTokenType.ASTERISK)) {
PsiMethod method = PsiTreeUtil.getParentOfType(ref, PsiMethod.class, true, PsiLambdaExpression.class, PsiClass.class);
if (MethodUtils.isHashCode(method)) {
// Standard hashCode template generates int result = 0; result = result * 31 + ...;
// so annoying warnings might be produced there
return false;
}
}
}
else if (ref instanceof PsiMethodCallExpression) {
PsiMethodCallExpression call = (PsiMethodCallExpression)ref;
PsiExpression qualifier = call.getMethodExpression().getQualifierExpression();
if (PsiUtil.isConstantExpression(qualifier) &&
ContainerUtil.and(call.getArgumentList().getExpressions(), PsiUtil::isConstantExpression)) {
return false;
}
}
else if (ref instanceof PsiTypeCastExpression) {
PsiExpression operand = ((PsiTypeCastExpression)ref).getOperand();
return operand != null && TypeConversionUtil.isFloatOrDoubleType(operand.getType());
}
else {
return false;
}
PsiElement parent = PsiUtil.skipParenthesizedExprUp(ref.getParent());
PsiBinaryExpression binOp = tryCast(parent, PsiBinaryExpression.class);
return binOp == null || !ComparisonUtils.isEqualityComparison(binOp) ||
(!ExpressionUtils.isZero(binOp.getLOperand()) && !ExpressionUtils.isZero(binOp.getROperand()));
}
private void reportConstantReferenceValue(ProblemsHolder reporter, PsiExpression ref, ConstantResult constant) {
if (!REPORT_CONSTANT_REFERENCE_VALUES && ref instanceof PsiReferenceExpression) return;
if (shouldBeSuppressed(ref) || constant == ConstantResult.UNKNOWN) return;
List<LocalQuickFix> fixes = new SmartList<>();
String presentableName = constant.toString();
if (Integer.valueOf(0).equals(constant.value()) && !shouldReportZero(ref)) return;
if (constant.value() instanceof Boolean) {
fixes.add(createSimplifyBooleanExpressionFix(ref, (Boolean)constant.value()));
} else {
fixes.add(new ReplaceWithConstantValueFix(presentableName, presentableName));
}
Object value = constant.value();
boolean isAssertion = isAssertionEffectively(ref, constant);
if (isAssertion && DONT_REPORT_TRUE_ASSERT_STATEMENTS) return;
if (value instanceof Boolean) {
ContainerUtil.addIfNotNull(fixes, createReplaceWithNullCheckFix(ref, (Boolean)value));
}
if (reporter.isOnTheFly()) {
if (ref instanceof PsiReferenceExpression) {
fixes.add(new SetInspectionOptionFix(this, "REPORT_CONSTANT_REFERENCE_VALUES",
JavaAnalysisBundle.message("inspection.data.flow.turn.off.constant.references.quickfix"),
false));
}
if (isAssertion) {
fixes.add(new SetInspectionOptionFix(this, "DONT_REPORT_TRUE_ASSERT_STATEMENTS",
JavaAnalysisBundle.message("inspection.data.flow.turn.off.true.asserts.quickfix"), true));
}
}
ContainerUtil.addIfNotNull(fixes, new FindDfaProblemCauseFix(IGNORE_ASSERT_STATEMENTS, ref, new TrackingRunner.ValueDfaProblemType(value)));
ProblemHighlightType type;
String message;
if (ref instanceof PsiMethodCallExpression || ref instanceof PsiPolyadicExpression || ref instanceof PsiTypeCastExpression) {
type = ProblemHighlightType.GENERIC_ERROR_OR_WARNING;
message = JavaAnalysisBundle.message("dataflow.message.constant.expression", presentableName);
}
else {
type = ProblemHighlightType.WEAK_WARNING;
message = JavaAnalysisBundle.message("dataflow.message.constant.value", presentableName);
}
reporter.registerProblem(ref, message, type, fixes.toArray(LocalQuickFix.EMPTY_ARRAY));
}
private static boolean isCoveredBySurroundingFix(PsiElement anchor, boolean evaluatesToTrue) {
PsiElement parent = PsiUtil.skipParenthesizedExprUp(anchor.getParent());
if (parent instanceof PsiPolyadicExpression) {
IElementType tokenType = ((PsiPolyadicExpression)parent).getOperationTokenType();
return tokenType.equals(JavaTokenType.ANDAND) && !evaluatesToTrue ||
tokenType.equals(JavaTokenType.OROR) && evaluatesToTrue;
}
return parent instanceof PsiExpression && BoolUtils.isNegation((PsiExpression)parent);
}
private static boolean isCondition(@NotNull PsiExpression expression) {
PsiType type = expression.getType();
if (type == null || !PsiType.BOOLEAN.isAssignableFrom(type)) return false;
if (!(expression instanceof PsiMethodCallExpression) && !(expression instanceof PsiReferenceExpression)) return true;
PsiElement parent = PsiUtil.skipParenthesizedExprUp(expression.getParent());
if (parent instanceof PsiStatement) return !(parent instanceof PsiReturnStatement);
if (parent instanceof PsiPolyadicExpression) {
IElementType tokenType = ((PsiPolyadicExpression)parent).getOperationTokenType();
return tokenType.equals(JavaTokenType.ANDAND) || tokenType.equals(JavaTokenType.OROR) ||
tokenType.equals(JavaTokenType.AND) || tokenType.equals(JavaTokenType.OR);
}
if (parent instanceof PsiConditionalExpression) {
return PsiTreeUtil.isAncestor(((PsiConditionalExpression)parent).getCondition(), expression, false);
}
return PsiUtil.isAccessedForWriting(expression);
}
@Contract("null -> false")
private static boolean shouldBeSuppressed(PsiElement anchor) {
if (!(anchor instanceof PsiExpression)) return false;
// Don't report System.out.println(b = false) or doSomething((Type)null)
if (anchor instanceof PsiAssignmentExpression ||
(anchor instanceof PsiTypeCastExpression && !(((PsiTypeCastExpression)anchor).getType() instanceof PsiPrimitiveType))) {
return true;
}
// For conditional the root cause (constant condition or both branches constant) should be already reported for branches
if (anchor instanceof PsiConditionalExpression) return true;
PsiExpression expression = (PsiExpression)anchor;
if (expression instanceof PsiReferenceExpression) {
PsiReferenceExpression ref = (PsiReferenceExpression)expression;
if ("TRUE".equals(ref.getReferenceName()) || "FALSE".equals(ref.getReferenceName())) {
PsiElement target = ref.resolve();
if (target instanceof PsiField) {
PsiClass containingClass = ((PsiField)target).getContainingClass();
if (containingClass != null && CommonClassNames.JAVA_LANG_BOOLEAN.equals(containingClass.getQualifiedName())) return true;
}
}
}
if (expression instanceof PsiBinaryExpression) {
PsiExpression lOperand = ((PsiBinaryExpression)expression).getLOperand();
PsiExpression rOperand = ((PsiBinaryExpression)expression).getROperand();
IElementType tokenType = ((PsiBinaryExpression)expression).getOperationTokenType();
// Suppress on type mismatch compilation errors
if (rOperand == null) return true;
PsiType lType = lOperand.getType();
PsiType rType = rOperand.getType();
if (lType == null || rType == null) return true;
if (!TypeConversionUtil.isBinaryOperatorApplicable(tokenType, lType, rType, false)) return true;
}
if (expression instanceof PsiInstanceOfExpression) {
PsiType type = ((PsiInstanceOfExpression)expression).getOperand().getType();
if (type == null || !TypeConstraints.instanceOf(type).isResolved()) return true;
PsiPattern pattern = ((PsiInstanceOfExpression)expression).getPattern();
if (pattern instanceof PsiTypeTestPattern && ((PsiTypeTestPattern)pattern).getPatternVariable() != null) {
PsiTypeElement checkType = ((PsiTypeTestPattern)pattern).getCheckType();
if (checkType != null && checkType.getType().isAssignableFrom(type)) {
// Reported as compilation error
return true;
}
}
}
PsiElement parent = PsiUtil.skipParenthesizedExprUp(expression.getParent());
// Don't report "x" in "x == null" as will be anyway reported as "always true"
if (parent instanceof PsiBinaryExpression && ExpressionUtils.getValueComparedWithNull((PsiBinaryExpression)parent) != null) return true;
// Dereference of null will be covered by other warning
if (ExpressionUtils.isVoidContext(expression) || isDereferenceContext(expression)) return true;
// We assume all Void variables as null because you cannot instantiate it without dirty hacks
// However reporting them as "always null" looks redundant (dereferences or comparisons will be reported though).
if (TypeUtils.typeEquals(CommonClassNames.JAVA_LANG_VOID, expression.getType())) return true;
if (isFlagCheck(anchor)) return true;
if (!isCondition(expression) && expression instanceof PsiMethodCallExpression) {
List<? extends MethodContract> contracts = JavaMethodContractUtil.getMethodCallContracts((PsiCallExpression)expression);
ContractReturnValue value = JavaMethodContractUtil.getNonFailingReturnValue(contracts);
if (value != null) return true;
if (!(parent instanceof PsiAssignmentExpression) && !(parent instanceof PsiVariable) &&
!(parent instanceof PsiReturnStatement)) {
PsiMethod method = ((PsiMethodCallExpression)expression).resolveMethod();
if (method == null || !JavaMethodContractUtil.isPure(method)) return true;
}
}
while (expression != null && BoolUtils.isNegation(expression)) {
expression = BoolUtils.getNegated(expression);
}
if (expression == null) return false;
if (!isCondition(expression) && expression instanceof PsiReferenceExpression) {
PsiVariable variable = tryCast(((PsiReferenceExpression)expression).resolve(), PsiVariable.class);
if (variable instanceof PsiField &&
variable.hasModifierProperty(PsiModifier.STATIC) &&
ExpressionUtils.isNullLiteral(PsiFieldImpl.getDetachedInitializer(variable))) {
return true;
}
if (variable instanceof PsiLocalVariable && variable.hasInitializer()) {
boolean effectivelyFinal = variable.hasModifierProperty(PsiModifier.FINAL) ||
!VariableAccessUtils.variableIsAssigned(variable, PsiUtil.getVariableCodeBlock(variable, null));
return effectivelyFinal && PsiUtil.isConstantExpression(variable.getInitializer());
}
return false;
}
// Avoid double reporting
return expression instanceof PsiMethodCallExpression && EqualsWithItselfInspection.isEqualsWithItself((PsiMethodCallExpression)expression) ||
expression instanceof PsiBinaryExpression && ComparisonToNaNInspection.extractNaNFromComparison((PsiBinaryExpression)expression) != null;
}
private static boolean isDereferenceContext(PsiExpression ref) {
PsiElement parent = PsiUtil.skipParenthesizedExprUp(ref.getParent());
return parent instanceof PsiReferenceExpression || parent instanceof PsiArrayAccessExpression
|| parent instanceof PsiSwitchStatement || parent instanceof PsiSynchronizedStatement;
}
private static boolean isFlagCheck(PsiElement element) {
PsiElement scope = PsiTreeUtil.getParentOfType(element, PsiStatement.class, PsiVariable.class);
PsiExpression topExpression = scope instanceof PsiIfStatement ? ((PsiIfStatement)scope).getCondition() :
scope instanceof PsiVariable ? ((PsiVariable)scope).getInitializer() :
null;
if (!PsiTreeUtil.isAncestor(topExpression, element, false)) return false;
return StreamEx.<PsiElement>ofTree(topExpression, e -> StreamEx.of(e.getChildren()))
.anyMatch(ConstantValueInspection::isCompileTimeFlagCheck);
}
private static boolean isCompileTimeFlagCheck(PsiElement element) {
if(element instanceof PsiBinaryExpression) {
PsiBinaryExpression binOp = (PsiBinaryExpression)element;
if(ComparisonUtils.isComparisonOperation(binOp.getOperationTokenType())) {
PsiExpression comparedWith = null;
if(ExpressionUtils.isLiteral(binOp.getROperand())) {
comparedWith = binOp.getLOperand();
} else if(ExpressionUtils.isLiteral(binOp.getLOperand())) {
comparedWith = binOp.getROperand();
}
comparedWith = PsiUtil.skipParenthesizedExprDown(comparedWith);
if (isConstantOfType(comparedWith, PsiType.INT, PsiType.LONG)) {
// like "if(DEBUG_LEVEL > 2)"
return true;
}
if(comparedWith instanceof PsiBinaryExpression) {
PsiBinaryExpression subOp = (PsiBinaryExpression)comparedWith;
if(subOp.getOperationTokenType().equals(JavaTokenType.AND)) {
PsiExpression left = PsiUtil.skipParenthesizedExprDown(subOp.getLOperand());
PsiExpression right = PsiUtil.skipParenthesizedExprDown(subOp.getROperand());
if(isConstantOfType(left, PsiType.INT, PsiType.LONG) ||
isConstantOfType(right, PsiType.INT, PsiType.LONG)) {
// like "if((FLAGS & SOME_FLAG) != 0)"
return true;
}
}
}
}
}
// like "if(DEBUG)"
return isConstantOfType(element, PsiType.BOOLEAN);
}
private static boolean isConstantOfType(PsiElement element, PsiPrimitiveType... types) {
PsiElement resolved = element instanceof PsiReferenceExpression ? ((PsiReferenceExpression)element).resolve() : null;
if (!(resolved instanceof PsiField)) return false;
PsiField field = (PsiField)resolved;
PsiModifierList modifierList = field.getModifierList();
if (modifierList == null ||
!modifierList.hasModifierProperty(PsiModifier.STATIC) ||
!modifierList.hasModifierProperty(PsiModifier.FINAL)) {
return false;
}
if (!ArrayUtil.contains(field.getType(), types)) return false;
return field.hasInitializer() && PsiUtil.isConstantExpression(field.getInitializer());
}
private static boolean isAtRHSOfBooleanAnd(PsiElement expr) {
PsiElement cur = expr;
while (cur != null && !(cur instanceof PsiMember)) {
PsiElement parent = cur.getParent();
if (parent instanceof PsiBinaryExpression && cur == ((PsiBinaryExpression)parent).getROperand()) {
return true;
}
cur = parent;
}
return false;
}
private void reportConstantBoolean(ProblemsHolder reporter, PsiElement psiAnchor, boolean evaluatesToTrue) {
while (psiAnchor instanceof PsiParenthesizedExpression parenthesized) {
psiAnchor = parenthesized.getExpression();
}
if (psiAnchor == null || shouldBeSuppressed(psiAnchor)) return;
boolean isAssertion = psiAnchor instanceof PsiExpression expr && DfaPsiUtil.isAssertionEffectively(expr, evaluatesToTrue);
if (DONT_REPORT_TRUE_ASSERT_STATEMENTS && isAssertion) return;
if (PsiUtil.skipParenthesizedExprUp(psiAnchor.getParent()) instanceof PsiAssignmentExpression assignment &&
PsiTreeUtil.isAncestor(assignment.getLExpression(), psiAnchor, false)) {
reporter.registerProblem(
psiAnchor,
JavaAnalysisBundle.message("dataflow.message.pointless.assignment.expression", Boolean.toString(evaluatesToTrue)),
createConditionalAssignmentFixes(evaluatesToTrue, assignment, reporter.isOnTheFly())
);
return;
}
List<LocalQuickFix> fixes = new ArrayList<>();
if (!isCoveredBySurroundingFix(psiAnchor, evaluatesToTrue)) {
ContainerUtil.addIfNotNull(fixes, createSimplifyBooleanExpressionFix(psiAnchor, evaluatesToTrue));
if (isAssertion && reporter.isOnTheFly()) {
fixes.add(new SetInspectionOptionFix(this, "DONT_REPORT_TRUE_ASSERT_STATEMENTS",
JavaAnalysisBundle.message("inspection.data.flow.turn.off.true.asserts.quickfix"), true));
}
ContainerUtil.addIfNotNull(fixes, createReplaceWithNullCheckFix(psiAnchor, evaluatesToTrue));
}
if (psiAnchor instanceof PsiExpression) {
ContainerUtil.addIfNotNull(fixes, new FindDfaProblemCauseFix(IGNORE_ASSERT_STATEMENTS,
(PsiExpression)psiAnchor, new TrackingRunner.ValueDfaProblemType(evaluatesToTrue)));
}
String message = JavaAnalysisBundle.message(isAtRHSOfBooleanAnd(psiAnchor) ?
"dataflow.message.constant.condition.when.reached" :
"dataflow.message.constant.condition", evaluatesToTrue ? 1 : 0);
reporter.registerProblem(psiAnchor, message, fixes.toArray(LocalQuickFix.EMPTY_ARRAY));
}
private @Nullable LocalQuickFix createSimplifyBooleanExpressionFix(PsiElement element, final boolean value) {
LocalQuickFixOnPsiElement fix = createSimplifyBooleanFix(element, value);
if (fix == null) return null;
final String text = fix.getText();
return new LocalQuickFix() {
@Override
public @NotNull String getName() {
return text;
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
final PsiElement psiElement = descriptor.getPsiElement();
if (psiElement == null) return;
final LocalQuickFixOnPsiElement fix = createSimplifyBooleanFix(psiElement, value);
if (fix == null) return;
try {
LOG.assertTrue(psiElement.isValid());
fix.applyFix();
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
}
@Override
public @NotNull String getFamilyName() {
return JavaAnalysisBundle.message("inspection.data.flow.simplify.boolean.expression.quickfix");
}
};
}
private static LocalQuickFix createReplaceWithNullCheckFix(PsiElement psiAnchor, boolean evaluatesToTrue) {
if (evaluatesToTrue) return null;
if (!(psiAnchor instanceof PsiMethodCallExpression)) return null;
final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)psiAnchor;
if (!MethodCallUtils.isEqualsCall(methodCallExpression)) return null;
PsiExpression arg = ArrayUtil.getFirstElement(methodCallExpression.getArgumentList().getExpressions());
if (!ExpressionUtils.isNullLiteral(arg)) return null;
PsiElement parent = PsiUtil.skipParenthesizedExprUp(psiAnchor.getParent());
return EqualsToEqualityFix.buildFix(methodCallExpression, parent instanceof PsiExpression && BoolUtils.isNegation((PsiExpression)parent));
}
private static LocalQuickFix[] createConditionalAssignmentFixes(boolean evaluatesToTrue,
PsiAssignmentExpression assignment,
final boolean onTheFly) {
IElementType op = assignment.getOperationTokenType();
boolean toRemove = op == JavaTokenType.ANDEQ && !evaluatesToTrue || op == JavaTokenType.OREQ && evaluatesToTrue;
if (toRemove && !onTheFly) {
return LocalQuickFix.EMPTY_ARRAY;
}
return new LocalQuickFix[]{toRemove ? new RemoveAssignmentFix() : new SimplifyToAssignmentFix()};
}
protected LocalQuickFixOnPsiElement createSimplifyBooleanFix(PsiElement element, boolean value) {
if (!(element instanceof PsiExpression)) return null;
if (PsiTreeUtil.findChildOfType(element, PsiAssignmentExpression.class) != null) return null;
final PsiExpression expression = (PsiExpression)element;
while (element.getParent() instanceof PsiExpression) {
element = element.getParent();
}
final SimplifyBooleanExpressionFix fix = new SimplifyBooleanExpressionFix(expression, value);
// simplify intention already active
if (!fix.isAvailable() || SimplifyBooleanExpressionFix.canBeSimplified((PsiExpression)element)) return null;
return fix;
}
/**
* @param anchor expression
* @param result the expected value of expression
* @return true if this expression is effectively an assertion (code throws if its value is not equal to expectedValue)
*/
private static boolean isAssertionEffectively(@NotNull PsiExpression anchor, ConstantResult result) {
Object value = result.value();
if (value instanceof Boolean) {
return DfaPsiUtil.isAssertionEffectively(anchor, (Boolean)value);
}
if (value != null) return false;
return DfaPsiUtil.isAssertCallArgument(anchor, ContractValue.nullValue());
}
/**
* Values that especially tracked by data flow inspection
*/
private enum ConstantResult {
TRUE, FALSE, NULL, ZERO, UNKNOWN;
@Override
public @NotNull String toString() {
return this == ZERO ? "0" : StringUtil.toLowerCase(name());
}
public Object value() {
return switch (this) {
case TRUE -> Boolean.TRUE;
case FALSE -> Boolean.FALSE;
case ZERO -> 0;
case NULL -> null;
default -> throw new UnsupportedOperationException();
};
}
public static @NotNull ConstantResult fromDfType(@NotNull DfType dfType) {
if (dfType == DfTypes.NULL) return NULL;
if (dfType == DfTypes.TRUE) return TRUE;
if (dfType == DfTypes.FALSE) return FALSE;
if (dfType.isConst(0) || dfType.isConst(0L)) return ZERO;
return UNKNOWN;
}
}
}

View File

@@ -0,0 +1,33 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInspection.dataFlow;
import com.intellij.codeInspection.ex.InspectionElementsMergerBase;
import com.intellij.openapi.util.JDOMExternalizerUtil;
import org.jdom.Element;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
public class ConstantValueInspectionMerger extends InspectionElementsMergerBase {
@Override
public @NotNull String getMergedToolName() {
return "ConstantValue";
}
@Override
public @NonNls String @NotNull [] getSourceToolNames() {
return new String[] {"ConstantConditions"};
}
@Override
protected Element transformElement(@NotNull String sourceToolName, @NotNull Element sourceElement, @NotNull Element toolElement) {
ConstantValueInspection inspection = new ConstantValueInspection();
inspection.DONT_REPORT_TRUE_ASSERT_STATEMENTS =
Boolean.parseBoolean(JDOMExternalizerUtil.readField(sourceElement, "DONT_REPORT_TRUE_ASSERT_STATEMENTS", "false"));
inspection.IGNORE_ASSERT_STATEMENTS =
Boolean.parseBoolean(JDOMExternalizerUtil.readField(sourceElement, "IGNORE_ASSERT_STATEMENTS", "false"));
inspection.REPORT_CONSTANT_REFERENCE_VALUES =
Boolean.parseBoolean(JDOMExternalizerUtil.readField(sourceElement, "REPORT_CONSTANT_REFERENCE_VALUES", "true"));
inspection.writeSettings(toolElement);
return toolElement;
}
}

View File

@@ -4,7 +4,6 @@ package com.intellij.codeInspection.dataFlow;
import com.intellij.codeInsight.NullableNotNullDialog;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightingFeature;
import com.intellij.codeInsight.daemon.impl.quickfix.DeleteSideEffectsAwareFix;
import com.intellij.codeInsight.daemon.impl.quickfix.SimplifyBooleanExpressionFix;
import com.intellij.codeInsight.daemon.impl.quickfix.UnwrapSwitchLabelFix;
import com.intellij.codeInspection.*;
import com.intellij.codeInspection.dataFlow.fix.BoxPrimitiveInTernaryFix;
@@ -18,9 +17,7 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.text.HtmlChunk;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiPrecedenceUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.IncorrectOperationException;
@@ -52,26 +49,11 @@ import static javax.swing.SwingConstants.TOP;
public class DataFlowInspection extends DataFlowInspectionBase {
private static final Logger LOG = Logger.getInstance(DataFlowInspection.class);
@Override
protected LocalQuickFix[] createConditionalAssignmentFixes(boolean evaluatesToTrue, PsiAssignmentExpression assignment, final boolean onTheFly) {
IElementType op = assignment.getOperationTokenType();
boolean toRemove = op == JavaTokenType.ANDEQ && !evaluatesToTrue || op == JavaTokenType.OREQ && evaluatesToTrue;
if (toRemove && !onTheFly) {
return LocalQuickFix.EMPTY_ARRAY;
}
return new LocalQuickFix[]{toRemove ? new RemoveAssignmentFix() : createSimplifyToAssignmentFix()};
}
@Override
public JComponent createOptionsPanel() {
return new OptionsPanel();
}
@Override
protected LocalQuickFix createReplaceWithTrivialLambdaFix(Object value) {
return new ReplaceWithTrivialLambdaFix(value);
}
@Override
protected LocalQuickFix createMutabilityViolationFix(PsiElement violation, boolean onTheFly) {
return WrapWithMutableCollectionFix.createFix(violation, onTheFly);
@@ -94,21 +76,6 @@ public class DataFlowInspection extends DataFlowInspectionBase {
return new IntroduceVariableFix(true);
}
@Override
protected LocalQuickFixOnPsiElement createSimplifyBooleanFix(PsiElement element, boolean value) {
if (!(element instanceof PsiExpression)) return null;
if (PsiTreeUtil.findChildOfType(element, PsiAssignmentExpression.class) != null) return null;
final PsiExpression expression = (PsiExpression)element;
while (element.getParent() instanceof PsiExpression) {
element = element.getParent();
}
final SimplifyBooleanExpressionFix fix = new SimplifyBooleanExpressionFix(expression, value);
// simplify intention already active
if (!fix.isAvailable() || SimplifyBooleanExpressionFix.canBeSimplified((PsiExpression)element)) return null;
return fix;
}
private static boolean isVolatileFieldReference(PsiExpression qualifier) {
PsiElement target = qualifier instanceof PsiReferenceExpression ? ((PsiReferenceExpression)qualifier).resolve() : null;
return target instanceof PsiField && ((PsiField)target).hasModifierProperty(PsiModifier.VOLATILE);
@@ -277,18 +244,10 @@ public class DataFlowInspection extends DataFlowInspectionBase {
message("inspection.data.flow.report.nullable.methods.that.always.return.a.non.null.value"),
"REPORT_NULLABLE_METHODS_RETURNING_NOT_NULL");
JCheckBox dontReportTrueAsserts = createCheckBoxWithHTML(
message("inspection.data.flow.true.asserts.option"),
"DONT_REPORT_TRUE_ASSERT_STATEMENTS");
JCheckBox ignoreAssertions = createCheckBoxWithHTML(
message("inspection.data.flow.ignore.assert.statements"),
"IGNORE_ASSERT_STATEMENTS");
JCheckBox reportConstantReferences = createCheckBoxWithHTML(
message("inspection.data.flow.warn.when.reading.a.value.guaranteed.to.be.constant"),
"REPORT_CONSTANT_REFERENCE_VALUES");
JCheckBox reportUnsoundWarnings = createCheckBoxWithHTML(
message("inspection.data.flow.report.problems.that.happen.only.on.some.code.paths"),
"REPORT_UNSOUND_WARNINGS");
@@ -316,15 +275,9 @@ public class DataFlowInspection extends DataFlowInspectionBase {
gc.gridy++;
add(reportNullableMethodsReturningNotNull, gc);
gc.gridy++;
add(dontReportTrueAsserts, gc);
gc.gridy++;
add(ignoreAssertions, gc);
gc.gridy++;
add(reportConstantReferences, gc);
gc.gridy++;
add(reportUnsoundWarnings, gc);
}

View File

@@ -1793,6 +1793,10 @@
<localInspection groupPath="Java" language="JAVA" shortName="ConstantConditions" bundle="messages.JavaBundle" key="inspection.data.flow.display.name"
groupKey="group.names.probable.bugs" groupBundle="messages.InspectionsBundle" enabledByDefault="true" level="WARNING"
implementationClass="com.intellij.codeInspection.dataFlow.DataFlowInspection"/>
<localInspection groupPath="Java" language="JAVA" shortName="ConstantValue" bundle="messages.JavaBundle" key="inspection.data.flow.constant.values.display.name"
groupKey="group.names.probable.bugs" groupBundle="messages.InspectionsBundle" enabledByDefault="true" level="WARNING"
implementationClass="com.intellij.codeInspection.dataFlow.ConstantValueInspection"/>
<inspectionElementsMerger implementation="com.intellij.codeInspection.dataFlow.ConstantValueInspectionMerger" />
<localInspection groupPath="Java" language="JAVA" shortName="Java9UndeclaredServiceUsage"
groupBundle="messages.InspectionsBundle" groupKey="group.names.visibility.issues"
enabledByDefault="true" level="WARNING"

View File

@@ -5,7 +5,7 @@ import com.intellij.codeInsight.Nullability;
import com.intellij.codeInsight.NullabilityAnnotationInfo;
import com.intellij.codeInsight.NullableNotNullManager;
import com.intellij.codeInsight.intention.AddAnnotationFix;
import com.intellij.codeInspection.dataFlow.DataFlowInspectionBase;
import com.intellij.codeInspection.dataFlow.DfaPsiUtil;
import com.intellij.codeInspection.dataFlow.DfaUtil;
import com.intellij.codeInspection.dataFlow.NullabilityUtil;
import com.intellij.codeInspection.dataFlow.inference.JavaSourceInference;
@@ -394,7 +394,7 @@ public class NullityInferrer {
opposite = lOperand;
}
if (opposite != null && opposite.getType() == PsiType.NULL) {
if (DataFlowInspectionBase.isAssertionEffectively(binOp, binOp.getOperationTokenType() == JavaTokenType.NE)) {
if (DfaPsiUtil.isAssertionEffectively(binOp, binOp.getOperationTokenType() == JavaTokenType.NE)) {
registerNotNullAnnotation(parameter);
return true;
}

View File

@@ -1,6 +1,6 @@
<html>
<body>
Reports code constructs that always produce the same result, may throw exceptions, or violates nullability contracts.
Reports code constructs that always violate nullability contracts, may throw exceptions, or just redundant, based on data flow analysis.
<p>Examples:</p>
<pre><code>if (array.length &lt; index) {
System.out.println(array[index]);
@@ -34,13 +34,9 @@ Integer square(@Nullable Integer input) {
null (e.g. immediately dereferenced in the method body), but there are call sites where a <code>null</code> literal is passed.</li>
<li>Use the <b>Report nullable methods that always return a non-null value</b> option to report methods that are annotated as
<code>@Nullable</code>, but always return non-null value. In this case, it's suggested that you change the annotation to <code>@NotNull</code>.</li>
<li>Use the <b>Don't report assertions with condition statically proven to be always true</b> option to avoid reporting assertions that were
statically proven to be always true. This also includes conditions like <code>if (alwaysFalseCondition) throw new IllegalArgumentException();</code>.</li>
<li>Use the <b>Ignore assert statements</b> option to control how the inspection treats <code>assert</code> statements. By default, the option
is disabled, which means that the assertions are assumed to be executed (-ea mode). If the option is enabled, the assertions will be completely ignored
(-da mode).</li>
<li>Use the <b>Warn when reading a value guaranteed to be constant</b> option to add warnings on reading variables that contain some constant values,
for example: <code>true</code>, <code>false</code>, or <code>null</code>.</li>
<li>Use the <b>Report problems that happen only on some code paths</b> option to control whether to report problems that may happen only
on some code path. If this option is disabled, warnings like <i>exception is possible</i> will not be reported. The inspection will report
only warnings like <i>exception will definitely occur</i>. This mode may greatly reduce the number of false-positives, especially if the code
@@ -48,6 +44,9 @@ Integer square(@Nullable Integer input) {
important problems in legacy code bases.
</li>
</ul>
<p>
Before IntelliJ IDEA 2022.3, this inspection was part of "Constant Conditions & Exceptions" inspection. Now, it split into two inspections:
"Constant Values" and "Nullability and data flow problems".
</p>
</body>
</html>

View File

@@ -0,0 +1,37 @@
<html>
<body>
Reports expressions and conditions that always produce the same result, like true, false, null, or zero.
Such expressions could be replaced with the corresponding constant value. Very often though they signal about a bug
in the code.
<p>Examples:</p>
<pre><code> // always true
// root cause: || is used instead of &&
if (x &gt; 0 || x &lt; 10) {}
System.out.println(str.trim());
// always false
// root cause: variable was dereferenced before null-check
if (str == null) {}
</code></pre>
<p>
The inspection behavior may be controlled by a number of annotations, such as
<a href="https://www.jetbrains.com/help/idea/nullable-and-notnull-annotations.html">nullability</a> annotations,
<code><a href="https://www.jetbrains.com/help/idea/contract-annotations.html">@Contract</a></code> annotation,
<code>@Range</code> annotation and so on.
</p>
<!-- tooltip end -->
<p>Configure the inspection:</p>
<ul>
<li>Use the <b>Don't report assertions with condition statically proven to be always true</b> option to avoid reporting assertions that were
statically proven to be always true. This also includes conditions like <code>if (alwaysFalseCondition) throw new IllegalArgumentException();</code>.</li>
<li>Use the <b>Ignore assert statements</b> option to control how the inspection treats <code>assert</code> statements. By default, the option
is disabled, which means that the assertions are assumed to be executed (-ea mode). If the option is enabled, the assertions will be completely ignored
(-da mode).</li>
<li>Use the <b>Warn when constant is stored in variable</b> option to display warnings when variable is used, whose value is known to be a constant.</li>
</ul>
<p>
Before IntelliJ IDEA 2022.3, this inspection was part of "Constant Conditions & Exceptions" inspection. Now, it split into two inspections:
"Constant Values" and "Nullability and data flow problems".
</p>
</body>
</html>

View File

@@ -1,7 +1,7 @@
// "Assert 'container != null'" "true-preview"
class A{
void test(){
Integer container = null;
Integer container = Math.random() > 0.5 ? null : 1.0;
int i = 0;
assert container != null;
for (int limit = container.intValue(); i < limit; i++){}

View File

@@ -1,7 +1,7 @@
// "Assert 'container != null'" "true-preview"
class A{
void test(){
Integer container = null;
Integer container = Math.random() > 0.5 ? null : 1.0;
int i = 0;
for (int limit = container.int<caret>Value(); i < limit; i++){}
}

View File

@@ -1,4 +1,4 @@
// "Fix all 'Constant conditions & exceptions' problems in file" "true"
// "Fix all 'Nullability and data flow problems' problems in file" "true"
class Main {
void t() {
int i = 5;

View File

@@ -1,4 +1,4 @@
// "Fix all 'Constant conditions & exceptions' problems in file" "true"
// "Fix all 'Nullability and data flow problems' problems in file" "true"
class Main {
void t() {
int i = 5;

View File

@@ -1,4 +1,4 @@
// "Fix all 'Constant conditions & exceptions' problems in file" "true"
// "Fix all 'Constant values' problems in file" "true"
public class Test {
void foo() {
int k = 0;

View File

@@ -1,4 +1,4 @@
// "Fix all 'Constant conditions & exceptions' problems in file" "true"
// "Fix all 'Constant values' problems in file" "true"
public class Test {
void foo1() {
int k = 0;

View File

@@ -1,4 +1,4 @@
// "Fix all 'Constant conditions & exceptions' problems in file" "true"
// "Fix all 'Constant values' problems in file" "true"
public class Test {
void foo2() {
int k = 0;

View File

@@ -1,4 +1,4 @@
// "Fix all 'Constant conditions & exceptions' problems in file" "true"
// "Fix all 'Constant values' problems in file" "true"
public class Test {
void foo2() {
int k = 0;

View File

@@ -1,4 +1,4 @@
// "Fix all 'Constant conditions & exceptions' problems in file" "true"
// "Fix all 'Constant values' problems in file" "true"
public class Test {
void foo() {
int k = 0;

View File

@@ -1,4 +1,4 @@
// "Fix all 'Constant conditions & exceptions' problems in file" "true"
// "Fix all 'Constant values' problems in file" "true"
public class Test {
void foo1() {
int k = 0;

View File

@@ -1,4 +1,4 @@
// "Fix all 'Constant conditions & exceptions' problems in file" "true"
// "Fix all 'Constant values' problems in file" "true"
public class Test {
void foo2() {
int k = 0;

View File

@@ -1,4 +1,4 @@
// "Fix all 'Constant conditions & exceptions' problems in file" "true"
// "Fix all 'Constant values' problems in file" "true"
public class Test {
void foo2() {
int k = 0;

View File

@@ -4,7 +4,7 @@ import java.lang.Integer;
class A{
void test(){
Integer integer = null;
Integer integer = Math.random() > 0.5 ? null : 1.0;
int i = integer != null ? integer.toString().length() : 0<caret>;
}
}

View File

@@ -4,7 +4,7 @@ import java.lang.Integer;
class A{
void test(){
Integer integer = null;
Integer integer = Math.random() > 0.5 ? null : 1.0;
int i = integer != null ? integer.intValue() : 0<caret>;
}
}

View File

@@ -4,7 +4,7 @@ import java.lang.Integer;
class A{
void test(){
Integer integer = null;
Integer integer = Math.random() > 0.5 ? null : 1.0;
int i = integer.to<caret>String().length();
}
}

View File

@@ -4,7 +4,7 @@ import java.lang.Integer;
class A{
void test(){
Integer integer = null;
Integer integer = Math.random() > 0.5 ? null : 1.0;
int i = integer.int<caret>Value();
}
}

View File

@@ -1,4 +1,4 @@
// "Fix all 'Constant conditions & exceptions' problems in file" "true"
// "Fix all 'Constant values' problems in file" "true"
import org.jetbrains.annotations.*;
import java.util.*;
@@ -11,7 +11,7 @@ public class MethodReferenceConstantValue {
}
public void test(Optional<String> opt) {
X x = MethodReferenceConstantValue::strangeMethod;
X x = (methodReferenceConstantValue, s1) -> false;
Boolean aBoolean = opt.map(s -> false)
.map(o1 -> true)
.map(o -> false)

View File

@@ -1,4 +1,4 @@
// "Fix all 'Constant conditions & exceptions' problems in file" "true"
// "Fix all 'Constant values' problems in file" "true"
import org.jetbrains.annotations.*;
import java.util.*;

View File

@@ -1,4 +1,4 @@
// "Fix all 'Constant conditions & exceptions' problems in file" "true"
// "Fix all 'Constant values' problems in file" "true"
import java.util.ArrayList;
import java.util.List;

View File

@@ -1,4 +1,4 @@
// "Fix all 'Constant conditions & exceptions' problems in file" "true"
// "Fix all 'Constant values' problems in file" "true"
import java.util.ArrayList;
import java.util.List;

View File

@@ -34,7 +34,7 @@ public class Example {
System.out.println("null");
}
@NotNull Class x = <warning descr="'null' is assigned to a variable that is annotated with @NotNull">ClassUtils.primitiveToWrapper(null)</warning>;
@NotNull Class x = <warning descr="'null' is assigned to a variable that is annotated with @NotNull"><warning descr="Result of 'ClassUtils.primitiveToWrapper(null)' is always 'null'">ClassUtils.primitiveToWrapper(null)</warning></warning>;
}
void writeBytes(@Nullable byte[] bytes, FilterOutputStream stream) throws IOException {

View File

@@ -21,7 +21,7 @@ public class MethodReferenceConstantValue {
}
public void test(Optional<String> opt) {
X x = <warning descr="Method reference invocation 'MethodReferenceConstantValue::strangeMethod' may produce 'NullPointerException'">MethodReferenceConstantValue::strangeMethod</warning>;
X x = <warning descr="Method reference invocation 'MethodReferenceConstantValue::strangeMethod' may produce 'NullPointerException'"><warning descr="Method reference result is always 'false'">MethodReferenceConstantValue::strangeMethod</warning></warning>;
Boolean aBoolean = opt.map(<warning descr="Method reference result is always 'false'">this::strangeMethod</warning>)
.map(<warning descr="Method reference result is always 'true'">Objects::nonNull</warning>)
.map(<warning descr="Method reference result is always 'false'">Objects::isNull</warning>)

View File

@@ -24,14 +24,14 @@ class Test {
void parens() {
Object x = null;
doNotNull(<warning descr="Passing 'null' argument to parameter annotated as @NotNull">x</warning>);
doNotNull(<warning descr="Passing 'null' argument to parameter annotated as @NotNull"><weak_warning descr="Value 'x' is always 'null'">x</weak_warning></warning>);
x = null;
doNotNull((<warning descr="Passing 'null' argument to parameter annotated as @NotNull">x</warning>));
doNotNull((<warning descr="Passing 'null' argument to parameter annotated as @NotNull"><weak_warning descr="Value 'x' is always 'null'">x</weak_warning></warning>));
}
@NotNull Object testReturn(Object x1, Object x2) {
if(x1 == null) return <warning descr="'null' is returned by the method declared as @NotNull">x1</warning>;
if(x2 == null) return (<warning descr="'null' is returned by the method declared as @NotNull">x2</warning>);
if(x1 == null) return <warning descr="'null' is returned by the method declared as @NotNull"><weak_warning descr="Value 'x1' is always 'null'">x1</weak_warning></warning>;
if(x2 == null) return (<warning descr="'null' is returned by the method declared as @NotNull"><weak_warning descr="Value 'x2' is always 'null'">x2</weak_warning></warning>);
return new Object();
}

View File

@@ -10,10 +10,10 @@ class Test {
if (foo == null) {
println(<weak_warning descr="Value 'foo' is always 'null'"><caret>foo</weak_warning>);
println(<weak_warning descr="Value 'foo' is always 'null'">foo</weak_warning>);
return <warning descr="'null' is returned by the method which is not declared as @Nullable">foo</warning>;
return <warning descr="'null' is returned by the method which is not declared as @Nullable"><weak_warning descr="Value 'foo' is always 'null'">foo</weak_warning></warning>;
}
if (bar == null) {
test2(<warning descr="Passing 'null' argument to parameter annotated as @NotNull">bar</warning>);
test2(<warning descr="Passing 'null' argument to parameter annotated as @NotNull"><weak_warning descr="Value 'bar' is always 'null'">bar</weak_warning></warning>);
}
return foo;
}

View File

@@ -0,0 +1,31 @@
class Test {
void noSuppress(int x) {
if (<warning descr="Condition 'x < 0 && x > 20' is always 'false'">x < 0 && <warning descr="Condition 'x > 20' is always 'false' when reached">x > 20</warning></warning>) {}
Object s = "23";
System.out.println(((<warning descr="Casting 's' to 'Number' will produce 'ClassCastException' for any non-null value">Number</warning>)s).byteValue());
}
void suppressOld(int x) {
//noinspection ConstantConditions
if (x < 0 && x > 20) {}
Object s = "23";
//noinspection ConstantConditions
System.out.println(((Number)s).byteValue());
}
void suppressNew(int x) {
//noinspection ConstantValue
if (x < 0 && x > 20) {}
Object s = "23";
//noinspection ConstantConditions
System.out.println(((Number)s).byteValue());
}
void wrongSuppress(int x) {
//noinspection ConstantValue
if (x < 0 && x > 20) {}
Object s = "23";
//noinspection ConstantValue
System.out.println(((<warning descr="Casting 's' to 'Number' will produce 'ClassCastException' for any non-null value">Number</warning>)s).byteValue());
}
}

View File

@@ -20,7 +20,7 @@ public class XorNullity {
}
if(createForm.getOpenIdIdentity() != null) {
findByOpenIdIdentity(<warning descr="Passing 'null' argument to parameter annotated as @NotNull">createForm.getOpenIdProvider()</warning>); // nullable
findByOpenIdIdentity(<warning descr="Passing 'null' argument to parameter annotated as @NotNull"><warning descr="Result of 'createForm.getOpenIdProvider()' is always 'null'">createForm.getOpenIdProvider()</warning></warning>); // nullable
}
}

View File

@@ -6,6 +6,7 @@ import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.codeInspection.dataFlow.ConstantValueInspection;
import com.intellij.codeInspection.dataFlow.DataFlowInspection;
import com.intellij.codeInspection.deadCode.UnusedDeclarationInspection;
import com.intellij.codeInspection.ex.GlobalInspectionToolWrapper;
@@ -25,7 +26,7 @@ public class FixAllQuickfixTest extends LightQuickFixParameterizedTestCase {
@Override
protected LocalInspectionTool @NotNull [] configureLocalInspectionTools() {
return new LocalInspectionTool[]{
new DataFlowInspection(),
new ConstantValueInspection(),
new UnnecessaryFullyQualifiedNameInspection(),
new MyLocalInspectionTool()
};

View File

@@ -3,6 +3,7 @@ package com.intellij.codeInsight.daemon.impl.quickfix;
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase;
import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.dataFlow.ConstantValueInspection;
import com.intellij.codeInspection.dataFlow.DataFlowInspection;
import com.intellij.testFramework.LightProjectDescriptor;
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;
@@ -11,7 +12,7 @@ import org.jetbrains.annotations.NotNull;
public class ReplaceWithConstantValueFixTest extends LightQuickFixParameterizedTestCase {
@Override
protected LocalInspectionTool @NotNull [] configureLocalInspectionTools() {
return new LocalInspectionTool[]{new DataFlowInspection()};
return new LocalInspectionTool[]{new ConstantValueInspection()};
}
@NotNull

View File

@@ -4,13 +4,13 @@ package com.intellij.codeInsight.daemon.impl.quickfix;
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase;
import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.dataFlow.DataFlowInspection;
import com.intellij.codeInspection.dataFlow.ConstantValueInspection;
import org.jetbrains.annotations.NotNull;
public class ReplaceWithNullCheckFixTest extends LightQuickFixParameterizedTestCase {
@Override
protected LocalInspectionTool @NotNull [] configureLocalInspectionTools() {
return new LocalInspectionTool[]{new DataFlowInspection()};
return new LocalInspectionTool[]{new ConstantValueInspection()};
}
@Override

View File

@@ -4,13 +4,14 @@ package com.intellij.codeInsight.daemon.impl.quickfix;
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase;
import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.dataFlow.ConstantValueInspection;
import com.intellij.codeInspection.dataFlow.DataFlowInspection;
import org.jetbrains.annotations.NotNull;
public class ReplaceWithTrivialLambdaFixTest extends LightQuickFixParameterizedTestCase {
@Override
protected LocalInspectionTool @NotNull [] configureLocalInspectionTools() {
return new LocalInspectionTool[]{new DataFlowInspection()};
return new LocalInspectionTool[]{new ConstantValueInspection()};
}
@Override

View File

@@ -3,6 +3,7 @@ package com.intellij.codeInsight.daemon.impl.quickfix;
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase;
import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.dataFlow.ConstantValueInspection;
import com.intellij.codeInspection.dataFlow.DataFlowInspection;
import org.jetbrains.annotations.NotNull;
@@ -10,9 +11,7 @@ public class SimplifyBooleanExpressionFixTest extends LightQuickFixParameterized
@Override
protected LocalInspectionTool @NotNull [] configureLocalInspectionTools() {
DataFlowInspection inspection = new DataFlowInspection();
inspection.SUGGEST_NULLABLE_ANNOTATIONS = true;
return new LocalInspectionTool[]{inspection};
return new LocalInspectionTool[]{new ConstantValueInspection()};
}
@Override

View File

@@ -4,6 +4,7 @@ package com.intellij.codeInsight.daemon.impl.quickfix;
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase;
import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.dataFlow.ConstantValueInspection;
import com.intellij.codeInspection.dataFlow.DataFlowInspection;
import com.intellij.testFramework.LightProjectDescriptor;
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;
@@ -12,7 +13,7 @@ import org.jetbrains.annotations.NotNull;
public class UnwrapIfStatementFixTest extends LightQuickFixParameterizedTestCase {
@Override
protected LocalInspectionTool @NotNull [] configureLocalInspectionTools() {
return new LocalInspectionTool[]{new DataFlowInspection()};
return new LocalInspectionTool[]{new DataFlowInspection(), new ConstantValueInspection()};
}
@NotNull

View File

@@ -4,6 +4,7 @@ package com.intellij.java.codeInspection;
import com.intellij.JavaTestUtil;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInsight.NullableNotNullManager;
import com.intellij.codeInspection.dataFlow.ConstantValueInspection;
import com.intellij.codeInspection.dataFlow.DataFlowInspection;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.project.Project;
@@ -36,7 +37,7 @@ public class DataFlowInspection8Test extends DataFlowInspectionTestCase {
public void testMethodReferenceOnNullable() { doTest(); }
public void testObjectsNonNullWithUnknownNullable() {
setupTypeUseAnnotations("typeUse", myFixture);
doTestWith(insp -> insp.TREAT_UNKNOWN_MEMBERS_AS_NULLABLE = true);
doTestWith((insp, __) -> insp.TREAT_UNKNOWN_MEMBERS_AS_NULLABLE = true);
}
public void testNullableVoidLambda() { doTest(); }
public void testNullableForeachVariable() { doTestWithCustomAnnotations(); }
@@ -108,7 +109,9 @@ public class DataFlowInspection8Test extends DataFlowInspectionTestCase {
setupCustomAnnotations();
DataFlowInspection inspection = new DataFlowInspection();
inspection.IGNORE_ASSERT_STATEMENTS = true;
myFixture.enableInspections(inspection);
ConstantValueInspection cvInspection = new ConstantValueInspection();
cvInspection.IGNORE_ASSERT_STATEMENTS = true;
myFixture.enableInspections(inspection, cvInspection);
myFixture.testHighlighting(true, false, true, getTestName(false) + ".java");
}
@@ -216,7 +219,7 @@ public class DataFlowInspection8Test extends DataFlowInspectionTestCase {
public void testStreamReduceLogicalAnd() { doTest(); }
public void testStreamSingleElementReduce() { doTest(); }
public void testRequireNonNullMethodRef() {
doTestWith(dfa -> dfa.SUGGEST_NULLABLE_ANNOTATIONS = true);
doTestWith((dfa, __) -> dfa.SUGGEST_NULLABLE_ANNOTATIONS = true);
}
public void testMapGetWithValueNullability() { doTestWithCustomAnnotations(); }
@@ -360,9 +363,9 @@ public class DataFlowInspection8Test extends DataFlowInspectionTestCase {
myFixture.addClass("package org.jetbrains.annotations;\nimport java.lang.annotation.*;\n" +
"@Target(ElementType.TYPE_USE)\n" +
"public @interface UnknownNullability { }");
doTestWith(insp -> insp.SUGGEST_NULLABLE_ANNOTATIONS = false);
doTestWith((insp, __) -> insp.SUGGEST_NULLABLE_ANNOTATIONS = false);
}
public void testReturnOrElseNull() { doTestWith(insp -> insp.REPORT_NULLABLE_METHODS_RETURNING_NOT_NULL = true); }
public void testReturnOrElseNull() { doTestWith((insp, __) -> insp.REPORT_NULLABLE_METHODS_RETURNING_NOT_NULL = true); }
public void testArrayIntersectionType() { doTest(); }
public void testFunctionType() { doTest(); }
public void testIteratorHasNextModifiesPrivateField() { doTest(); }

View File

@@ -2,6 +2,7 @@
package com.intellij.java.codeInspection;
import com.intellij.JavaTestUtil;
import com.intellij.codeInspection.dataFlow.ConstantValueInspection;
import com.intellij.codeInspection.dataFlow.DataFlowInspection;
import com.intellij.testFramework.LightProjectDescriptor;
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;
@@ -21,8 +22,9 @@ public class DataFlowInspectionAncientTest extends LightJavaCodeInsightFixtureTe
private void doTest() {
DataFlowInspection inspection = new DataFlowInspection();
inspection.REPORT_CONSTANT_REFERENCE_VALUES = false;
myFixture.enableInspections(inspection);
ConstantValueInspection cvInspection = new ConstantValueInspection();
cvInspection.REPORT_CONSTANT_REFERENCE_VALUES = false;
myFixture.enableInspections(inspection, cvInspection);
myFixture.testHighlighting(getTestName(false) + ".java");
}

View File

@@ -16,6 +16,7 @@
package com.intellij.java.codeInspection
import com.intellij.JavaTestUtil
import com.intellij.codeInspection.dataFlow.ConstantValueInspection
import com.intellij.codeInspection.dataFlow.DataFlowInspection
import com.intellij.openapi.module.StdModuleTypes
import com.intellij.openapi.roots.ModuleRootManager
@@ -69,7 +70,7 @@ class DataFlowInspectionHeavyTest extends JavaCodeInsightFixtureTestCase {
}
'''
myFixture.configureFromExistingVirtualFile(testFile.virtualFile)
myFixture.enableInspections(new DataFlowInspection())
myFixture.enableInspections(new ConstantValueInspection())
myFixture.checkHighlighting()
}

View File

@@ -4,6 +4,7 @@ package com.intellij.java.codeInspection;
import com.intellij.JavaTestUtil;
import com.intellij.codeInsight.daemon.ImplicitUsageProvider;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInspection.dataFlow.ConstantValueInspection;
import com.intellij.codeInspection.dataFlow.DataFlowInspection;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
@@ -135,25 +136,25 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase {
public void testPassingNullableIntoVararg() { doTest(); }
public void testEqualsImpliesNotNull() {
doTestWith(i -> i.SUGGEST_NULLABLE_ANNOTATIONS = true);
doTestWith((i, __) -> i.SUGGEST_NULLABLE_ANNOTATIONS = true);
}
public void testEffectivelyUnqualified() { doTest(); }
public void testQualifierEquality() { doTest(); }
public void testSkipAssertions() {
doTestWith(i -> {
doTestWith((__, i) -> {
i.DONT_REPORT_TRUE_ASSERT_STATEMENTS = true;
i.REPORT_CONSTANT_REFERENCE_VALUES = true;
});
}
public void testParanoidMode() {
doTestWith(i -> i.TREAT_UNKNOWN_MEMBERS_AS_NULLABLE = true);
doTestWith((i, __) -> i.TREAT_UNKNOWN_MEMBERS_AS_NULLABLE = true);
}
public void testReportConstantReferences() {
doTestWith(i -> i.SUGGEST_NULLABLE_ANNOTATIONS = true);
doTestWith((i, __) -> i.SUGGEST_NULLABLE_ANNOTATIONS = true);
String hint = "Replace with 'null'";
checkIntentionResult(hint);
}
@@ -165,12 +166,12 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase {
}
public void testReportConstantReferences_OverloadedCall() {
doTestWith(i -> i.SUGGEST_NULLABLE_ANNOTATIONS = true);
doTestWith((i, __) -> i.SUGGEST_NULLABLE_ANNOTATIONS = true);
checkIntentionResult("Replace with 'null'");
}
public void testReportConstantReferencesAfterFinalFieldAccess() {
doTestWith(i -> i.SUGGEST_NULLABLE_ANNOTATIONS = true);
doTestWith((i, __) -> i.SUGGEST_NULLABLE_ANNOTATIONS = true);
}
public void testCheckFieldInitializers() {
@@ -204,9 +205,9 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase {
public void testFinalFieldInConstructorAnonymous() { doTest(); }
public void testFinalFieldNotDuringInitialization() {
doTestWith(i -> {
doTestWith((i, cv) -> {
i.TREAT_UNKNOWN_MEMBERS_AS_NULLABLE = true;
i.REPORT_CONSTANT_REFERENCE_VALUES = false;
cv.REPORT_CONSTANT_REFERENCE_VALUES = false;
});
}
@@ -230,7 +231,7 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase {
public void testHonorGetterAnnotation() { doTest(); }
public void testIgnoreAssertions() {
doTestWith(i -> i.IGNORE_ASSERT_STATEMENTS = true);
doTestWith((__, i) -> i.IGNORE_ASSERT_STATEMENTS = true);
}
public void testContractAnnotation() { doTest(); }
@@ -350,7 +351,7 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase {
public void testNullabilityDefaultVsMethodImplementing() {
addJavaxDefaultNullabilityAnnotations(myFixture);
doTestWith(i -> i.TREAT_UNKNOWN_MEMBERS_AS_NULLABLE = true);
doTestWith((i, __) -> i.TREAT_UNKNOWN_MEMBERS_AS_NULLABLE = true);
}
public void testTypeQualifierNickname() {
@@ -412,7 +413,7 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase {
myFixture.addClass("package foo; public class AnotherPackageNotNull { public static native Object foo(String s); }");
myFixture.addFileToProject("foo/package-info.java", "@bar.MethodsAreNotNullByDefault package foo;");
myFixture.enableInspections(new DataFlowInspection());
myFixture.enableInspections(new DataFlowInspection(), new ConstantValueInspection());
myFixture.testHighlighting(true, false, true, getTestName(false) + ".java");
}
@@ -427,7 +428,7 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase {
myFixture.addFileToProject("foo/package-info.java", "@NonnullByDefault package foo;");
myFixture.configureFromExistingVirtualFile(myFixture.copyFileToProject(getTestName(false) + ".java", "foo/Classes.java"));
myFixture.enableInspections(new DataFlowInspection());
myFixture.enableInspections(new DataFlowInspection(), new ConstantValueInspection());
myFixture.checkHighlighting(true, false, true);
}
@@ -471,7 +472,7 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase {
}
public void testLiteralDoWhileConditionWithBreak() {
doTest();
assertFalse(myFixture.getAvailableIntentions().stream().anyMatch(i -> i.getText().contains("Unwrap 'do-while' statement")));
assertFalse(ContainerUtil.exists(myFixture.getAvailableIntentions(), i -> i.getText().contains("Unwrap 'do-while' statement")));
}
public void testFalseForConditionNoInitialization() {
@@ -520,7 +521,7 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase {
public void testNullableMethodReturningNotNull() { doTest(); }
public void testDivisionByZero() {
doTestWith(i -> i.SUGGEST_NULLABLE_ANNOTATIONS = true);
doTestWith((i, __) -> i.SUGGEST_NULLABLE_ANNOTATIONS = true);
}
public void testFieldUsedBeforeInitialization() { doTest(); }
@@ -607,12 +608,12 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase {
public void testEqualsInLoopNotTooComplex() { doTest(); }
public void testEqualsWithItself() { doTest(); }
public void testBoxingBoolean() {
doTestWith(i -> i.REPORT_CONSTANT_REFERENCE_VALUES = true);
doTestWith((__, i) -> i.REPORT_CONSTANT_REFERENCE_VALUES = true);
}
public void testOrWithAssignment() { doTest(); }
public void testAndAndLastOperand() { doTest(); }
public void testReportAlwaysNull() {
doTestWith(i -> i.REPORT_CONSTANT_REFERENCE_VALUES = true);
doTestWith((__, i) -> i.REPORT_CONSTANT_REFERENCE_VALUES = true);
}
public void testBoxUnboxArrayElement() { doTest(); }
@@ -701,7 +702,7 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase {
public void testFieldUpdateViaSetter() { doTest(); }
public void testInitArrayInConstructor() { doTest(); }
public void testGetterNullityAfterCheck() { doTest(); }
public void testInferenceNullityMismatch() { doTestWith(insp -> insp.SUGGEST_NULLABLE_ANNOTATIONS = false); }
public void testInferenceNullityMismatch() { doTestWith((insp, __) -> insp.SUGGEST_NULLABLE_ANNOTATIONS = false); }
public void testFieldInInstanceInitializer() { doTest(); }
public void testNullableCallWithPrecalculatedValueAndSpecialField() { doTest(); }
public void testJoinConstantAndSubtype() { doTest(); }
@@ -709,7 +710,7 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase {
public void testArrayInitializerElementRewritten() { doTest(); }
public void testFinallyEphemeralNpe() { doTest(); }
public void testTypeParameterAsSuperClass() { doTest(); }
public void testSuppressConstantBooleans() { doTestWith(insp -> insp.REPORT_CONSTANT_REFERENCE_VALUES = true); }
public void testSuppressConstantBooleans() { doTestWith((__, insp) -> insp.REPORT_CONSTANT_REFERENCE_VALUES = true); }
public void testTempVarsInContracts() { doTest(); }
public void testNestedUnrolledLoopNotComplex() { doTest(); }
public void testEnumOrdinal() { doTest(); }
@@ -722,4 +723,5 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase {
public void testBoxingInArrayDeclaration() { doTest(); }
public void testNestedVersusSuper() { doTest(); }
public void testChangeFieldUsedInPureMethod() { doTest(); }
public void testSuppression() { doTest(); }
}

View File

@@ -15,27 +15,31 @@
*/
package com.intellij.java.codeInspection;
import com.intellij.codeInspection.dataFlow.ConstantValueInspection;
import com.intellij.codeInspection.dataFlow.DataFlowInspection;
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;
import com.intellij.util.containers.ContainerUtil;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
public abstract class DataFlowInspectionTestCase extends LightJavaCodeInsightFixtureTestCase {
protected void doTest() {
doTestWith(i -> {
i.SUGGEST_NULLABLE_ANNOTATIONS = true;
i.REPORT_CONSTANT_REFERENCE_VALUES = false;
doTestWith((df, cv) -> {
df.SUGGEST_NULLABLE_ANNOTATIONS = true;
cv.REPORT_CONSTANT_REFERENCE_VALUES = false;
});
}
protected void doTestWith(Consumer<? super DataFlowInspection> inspectionMutator) {
protected void doTestWith(BiConsumer<DataFlowInspection, ConstantValueInspection> inspectionMutator) {
DataFlowInspection inspection = new DataFlowInspection();
inspectionMutator.accept(inspection);
myFixture.enableInspections(inspection);
ConstantValueInspection cvInspection = new ConstantValueInspection();
inspectionMutator.accept(inspection, cvInspection);
myFixture.enableInspections(inspection, cvInspection);
myFixture.testHighlighting(true, false, true, getTestName(false) + ".java");
}
public void assertIntentionAvailable(String intentionName) {
assertTrue(myFixture.getAvailableIntentions().stream().anyMatch(action -> action.getText().equals(intentionName)));
assertTrue(ContainerUtil.exists(myFixture.getAvailableIntentions(), action -> action.getText().equals(intentionName)));
}
}

View File

@@ -16,6 +16,7 @@
package com.intellij.java.codeInspection;
import com.intellij.JavaTestUtil;
import com.intellij.codeInspection.dataFlow.ConstantValueInspection;
import com.intellij.codeInspection.dataFlow.DataFlowInspection;
import com.intellij.testFramework.LightProjectDescriptor;
import org.jetbrains.annotations.NotNull;
@@ -37,7 +38,7 @@ public class HardcodedContractsTest extends DataFlowInspectionTestCase {
private void checkHighlighting() {
myFixture.enableInspections(new DataFlowInspection());
myFixture.enableInspections(new DataFlowInspection(), new ConstantValueInspection());
myFixture.testHighlighting(true, false, true, getTestName(false) + ".java");
}

View File

@@ -3,6 +3,7 @@ package com.intellij.java.codeInspection;
import com.intellij.analysis.AnalysisScope;
import com.intellij.codeInspection.InspectionManager;
import com.intellij.codeInspection.dataFlow.ConstantValueInspection;
import com.intellij.codeInspection.dataFlow.DataFlowInspection;
import com.intellij.codeInspection.ex.*;
import com.intellij.java.testFramework.fixtures.LightJava9ModulesCodeInsightFixtureTestCase;
@@ -71,69 +72,69 @@ public class InspectionResultExportTest extends LightJava9ModulesCodeInsightFixt
ProgressManager.getInstance().runProcess(() -> context.launchInspectionsOffline(scope, outputPath, false, resultFiles), new ProgressWindow(false, getProject()));
assertSize(2, resultFiles);
Element dfaResults = resultFiles.stream().filter(f -> f.getFileName().toString().equals("ConstantConditions.xml")).findAny().map(InspectionResultExportTest::loadFile).orElseThrow(AssertionError::new);
Element dfaResults = resultFiles.stream().filter(f -> f.getFileName().toString().equals("ConstantValue.xml")).findAny().map(InspectionResultExportTest::loadFile).orElseThrow(AssertionError::new);
Element unnCondResults = resultFiles.stream().filter(f -> f.getFileName().toString().equals("SimplifiableConditionalExpression.xml")).findAny().map(InspectionResultExportTest::loadFile).orElseThrow(AssertionError::new);
Element expectedDfaResults = JDOMUtil.load("<problems>" +
"<problem>\n" +
" <file>Foo.java</file>\n" +
" <line>6</line>\n" +
" <problem_class>Constant conditions &amp; exceptions</problem_class>\n" +
" <problem_class>Constant values</problem_class>\n" +
" <description>Condition &lt;code&gt;0 == 0&lt;/code&gt; is always &lt;code&gt;true&lt;/code&gt;</description>\n" +
"</problem>\n" +
"<problem>\n" +
" <file>Foo.java</file>\n" +
" <line>7</line>\n" +
" <problem_class>Constant conditions &amp; exceptions</problem_class>\n" +
" <problem_class>Constant values</problem_class>\n" +
" <description>Condition &lt;code&gt;0 == 0&lt;/code&gt; is always &lt;code&gt;true&lt;/code&gt;</description>\n" +
"</problem>\n" +
"<problem>\n" +
" <file>Foo.java</file>\n" +
" <line>8</line>\n" +
" <problem_class>Constant conditions &amp; exceptions</problem_class>\n" +
" <problem_class>Constant values</problem_class>\n" +
" <description>Condition &lt;code&gt;0 == 0&lt;/code&gt; is always &lt;code&gt;true&lt;/code&gt;</description>\n" +
"</problem>\n" +
"<problem>\n" +
" <file>Foo.java</file>\n" +
" <line>4</line>\n" +
" <problem_class>Constant conditions &amp; exceptions</problem_class>\n" +
" <problem_class>Constant values</problem_class>\n" +
" <description>Condition &lt;code&gt;0 == 0&lt;/code&gt; is always &lt;code&gt;true&lt;/code&gt;</description>\n" +
"</problem>\n" +
"<problem>\n" +
" <file>Foo.java</file>\n" +
" <line>5</line>\n" +
" <problem_class>Constant conditions &amp; exceptions</problem_class>\n" +
" <problem_class>Constant values</problem_class>\n" +
" <description>Condition &lt;code&gt;0 == 0&lt;/code&gt; is always &lt;code&gt;true&lt;/code&gt;</description>\n" +
"</problem>" +
"</problems>");
Element expectedUnnCondResults = JDOMUtil.load("<problems><problem>\n" +
" <file>Foo.java</file>\n" +
" <line>4</line>\n" +
" <problem_class>Redundant conditional expression</problem_class>\n" +
" <problem_class>Simplifiable conditional expression</problem_class>\n" +
" <description>&lt;code&gt;0 == 0 ? 0 : 0&lt;/code&gt; can be simplified to '0' #loc</description>\n" +
"</problem>\n" +
"<problem>\n" +
" <file>Foo.java</file>\n" +
" <line>5</line>\n" +
" <problem_class>Redundant conditional expression</problem_class>\n" +
" <problem_class>Simplifiable conditional expression</problem_class>\n" +
" <description>&lt;code&gt;0 == 0 ? 0 : 0&lt;/code&gt; can be simplified to '0' #loc</description>\n" +
"</problem>\n" +
"<problem>\n" +
" <file>Foo.java</file>\n" +
" <line>6</line>\n" +
" <problem_class>Redundant conditional expression</problem_class>\n" +
" <problem_class>Simplifiable conditional expression</problem_class>\n" +
" <description>&lt;code&gt;0 == 0 ? 0 : 0&lt;/code&gt; can be simplified to '0' #loc</description>\n" +
"</problem>\n" +
"<problem>\n" +
" <file>Foo.java</file>\n" +
" <line>7</line>\n" +
" <problem_class>Redundant conditional expression</problem_class>\n" +
" <problem_class>Simplifiable conditional expression</problem_class>\n" +
" <description>&lt;code&gt;0 == 0 ? 0 : 0&lt;/code&gt; can be simplified to '0' #loc</description>\n" +
"</problem>\n" +
"<problem>\n" +
" <file>Foo.java</file>\n" +
" <line>8</line>\n" +
" <problem_class>Redundant conditional expression</problem_class>\n" +
" <problem_class>Simplifiable conditional expression</problem_class>\n" +
" <description>&lt;code&gt;0 == 0 ? 0 : 0&lt;/code&gt; can be simplified to '0' #loc</description>\n" +
"</problem></problems>");
@@ -156,6 +157,6 @@ public class InspectionResultExportTest extends LightJava9ModulesCodeInsightFixt
}
private static @NotNull List<InspectionToolWrapper<?, ?>> getTools() {
return Arrays.asList(new LocalInspectionToolWrapper(new DataFlowInspection()), new LocalInspectionToolWrapper(new SimplifiableConditionalExpressionInspection()));
return Arrays.asList(new LocalInspectionToolWrapper(new ConstantValueInspection()), new LocalInspectionToolWrapper(new SimplifiableConditionalExpressionInspection()));
}
}

View File

@@ -113,8 +113,7 @@ public class JSpecifyAnnotationOldTest extends LightJavaCodeInsightFixtureTestCa
@Override
protected void reportNullabilityProblems(ProblemReporter reporter,
List<NullabilityProblemKind.NullabilityProblem<?>> problems,
Map<PsiExpression, ConstantResult> expressions) {
List<NullabilityProblemKind.NullabilityProblem<?>> problems) {
for (NullabilityProblemKind.NullabilityProblem<?> problem : problems) {
PsiExpression expression = problem.getDereferencedExpression();
if (expression != null) {

View File

@@ -219,8 +219,7 @@ public class JSpecifyAnnotationTest extends LightJavaCodeInsightFixtureTestCase
@Override
protected void reportNullabilityProblems(DataFlowInspectionBase.ProblemReporter reporter,
List<NullabilityProblemKind.NullabilityProblem<?>> problems,
Map<PsiExpression, DataFlowInspectionBase.ConstantResult> expressions) {
List<NullabilityProblemKind.NullabilityProblem<?>> problems) {
for (NullabilityProblemKind.NullabilityProblem<?> problem : problems) {
String warning = getJSpecifyWarning(problem);
if (warning != null) {

View File

@@ -379,12 +379,13 @@ inspection.conditional.break.in.infinite.loop.no.conversion.with.do.while=Don't
inspection.conditional.break.in.infinite.loop.allow.condition.fusion=Allow merging with existing loop condition
inspection.conditional.break.in.infinite.loop.suggest.conversion.when.if.is.single.stmt.in.loop=Suggest conversion when 'if' is a single statement in loop
inspection.convert.to.local.quickfix=Convert to local
inspection.data.flow.display.name=Constant conditions \\& exceptions
inspection.data.flow.display.name=Nullability and data flow problems
inspection.data.flow.constant.values.display.name=Constant values
inspection.data.flow.filter.notnull.quickfix=Insert 'filter(Objects::nonNull)' step
inspection.data.flow.nullable.quickfix.option=Suggest @Nullable annotation for methods/fields/parameters where nullable values are used
inspection.data.flow.true.asserts.option=Don't report assertions with condition statically proven to be always <code>true</code>
inspection.data.flow.ignore.assert.statements=Ignore assert statements
inspection.data.flow.warn.when.reading.a.value.guaranteed.to.be.constant=Warn when reading a value guaranteed to be constant
inspection.data.flow.warn.when.reading.a.value.guaranteed.to.be.constant=Warn when constant is stored in variable
inspection.data.flow.treat.non.annotated.members.and.parameters.as.nullable=Treat non-annotated members and parameters as @Nullable
inspection.data.flow.report.not.null.required.parameter.with.null.literal.argument.usages=Report not-null required parameter with null-literal argument usages
inspection.data.flow.report.nullable.methods.that.always.return.a.non.null.value=Report nullable methods that always return a non-null value

View File

@@ -1,6 +1,7 @@
package de.plushnikov.intellij.plugin.inspection;
import com.intellij.codeInspection.InspectionProfileEntry;
import com.intellij.codeInspection.dataFlow.ConstantValueInspection;
import com.intellij.codeInspection.dataFlow.DataFlowInspection;
@@ -13,7 +14,7 @@ public class DataFlowInspectionTest extends LombokInspectionTest {
@Override
protected InspectionProfileEntry getInspection() {
return new DataFlowInspection();
return new ConstantValueInspection();
}
public void testDefaultBuilderFinalValueInspectionIsAlwaysThat() {