mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-10-06 06:21:16 +07:00
DataFlowInspection: ProblemReporter to avoid duplicate reports on the same anchor
This commit is contained in:
+150
-146
@@ -262,29 +262,29 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
|
||||
allProblems.addAll(falseSet);
|
||||
StreamEx.of(runner.getInstructions()).select(InstanceofInstruction.class).filter(visitor::isInstanceofRedundant).into(allProblems);
|
||||
|
||||
HashSet<PsiElement> reportedAnchors = new HashSet<>();
|
||||
ProblemReporter reporter = new ProblemReporter(holder);
|
||||
|
||||
reportFailingCasts(holder, visitor, reportedAnchors);
|
||||
reportFailingCasts(reporter, visitor);
|
||||
reportUnreachableSwitchBranches(trueSet, falseSet, holder);
|
||||
|
||||
for (Instruction instruction : allProblems) {
|
||||
if (instruction instanceof BranchingInstruction) {
|
||||
handleBranchingInstruction(holder, visitor, trueSet, reportedAnchors, (BranchingInstruction)instruction);
|
||||
handleBranchingInstruction(reporter, visitor, trueSet, (BranchingInstruction)instruction);
|
||||
}
|
||||
}
|
||||
|
||||
reportAlwaysFailingCalls(holder, visitor, reportedAnchors);
|
||||
reportAlwaysFailingCalls(reporter, visitor);
|
||||
|
||||
reportNullabilityProblems(holder, visitor, reportedAnchors);
|
||||
reportNullableReturns(visitor, holder, reportedAnchors, scope);
|
||||
reportNullabilityProblems(reporter, visitor);
|
||||
reportNullableReturns(visitor, reporter, scope);
|
||||
if (SUGGEST_NULLABLE_ANNOTATIONS) {
|
||||
reportNullableArgumentsPassedToNonAnnotated(visitor, holder, reportedAnchors);
|
||||
reportNullableAssignedToNonAnnotatedFields(visitor, holder, reportedAnchors);
|
||||
reportNullableArgumentsPassedToNonAnnotated(visitor, reporter);
|
||||
reportNullableAssignedToNonAnnotatedFields(visitor, reporter);
|
||||
}
|
||||
|
||||
reportOptionalOfNullableImprovements(holder, reportedAnchors, visitor.getOfNullableCalls());
|
||||
reportOptionalOfNullableImprovements(reporter, visitor.getOfNullableCalls());
|
||||
|
||||
reportConstants(holder, visitor, reportedAnchors);
|
||||
reportConstants(reporter, visitor);
|
||||
|
||||
reportMethodReferenceProblems(holder, visitor);
|
||||
|
||||
@@ -296,13 +296,13 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
|
||||
reportAlwaysReturnsNotNull(holder, scope);
|
||||
}
|
||||
|
||||
reportMutabilityViolations(holder, reportedAnchors, visitor.getMutabilityViolations(true),
|
||||
reportMutabilityViolations(holder, visitor.getMutabilityViolations(true),
|
||||
InspectionsBundle.message("dataflow.message.immutable.modified"));
|
||||
reportMutabilityViolations(holder, reportedAnchors, visitor.getMutabilityViolations(false),
|
||||
reportMutabilityViolations(holder, visitor.getMutabilityViolations(false),
|
||||
InspectionsBundle.message("dataflow.message.immutable.passed"));
|
||||
|
||||
reportDuplicateAssignments(holder, reportedAnchors, visitor);
|
||||
reportPointlessSameArguments(holder, reportedAnchors, visitor);
|
||||
reportDuplicateAssignments(reporter, visitor);
|
||||
reportPointlessSameArguments(reporter, visitor);
|
||||
}
|
||||
|
||||
private void reportUnreachableSwitchBranches(Set<Instruction> trueSet, Set<Instruction> falseSet, ProblemsHolder holder) {
|
||||
@@ -336,16 +336,16 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
|
||||
}
|
||||
}
|
||||
|
||||
private void reportConstants(ProblemsHolder holder, DataFlowInstructionVisitor visitor, HashSet<PsiElement> reportedAnchors) {
|
||||
private void reportConstants(ProblemReporter reporter, DataFlowInstructionVisitor visitor) {
|
||||
visitor.getConstantExpressions().forEach((expression, result) -> {
|
||||
if (result == ConstantResult.UNKNOWN) return;
|
||||
if (isCondition(expression)) {
|
||||
if (result.value() instanceof Boolean) {
|
||||
reportConstantBoolean(holder, expression, reportedAnchors, (Boolean)result.value());
|
||||
reportConstantBoolean(reporter, expression, (Boolean)result.value());
|
||||
}
|
||||
}
|
||||
else {
|
||||
reportConstantReferenceValue(holder, reportedAnchors, expression, result);
|
||||
reportConstantReferenceValue(reporter, expression, result);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -366,11 +366,9 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
|
||||
return false;
|
||||
}
|
||||
|
||||
private void reportConstantReferenceValue(ProblemsHolder holder, Set<PsiElement> reportedAnchors,
|
||||
PsiExpression ref, ConstantResult constant) {
|
||||
private void reportConstantReferenceValue(ProblemReporter reporter, PsiExpression ref, ConstantResult constant) {
|
||||
if (!REPORT_CONSTANT_REFERENCE_VALUES && ref instanceof PsiReferenceExpression) return;
|
||||
if (shouldBeSuppressed(ref)) return;
|
||||
if (constant == ConstantResult.UNKNOWN || !reportedAnchors.add(ref)) return;
|
||||
if (shouldBeSuppressed(ref) || constant == ConstantResult.UNKNOWN) return;
|
||||
List<LocalQuickFix> fixes = new SmartList<>();
|
||||
String presentableName = constant.toString();
|
||||
fixes.add(new ReplaceWithConstantValueFix(presentableName, presentableName));
|
||||
@@ -380,7 +378,7 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
|
||||
if (value instanceof Boolean) {
|
||||
ContainerUtil.addIfNotNull(fixes, createReplaceWithNullCheckFix(ref, (Boolean)value));
|
||||
}
|
||||
if (holder.isOnTheFly()) {
|
||||
if (reporter.isOnTheFly()) {
|
||||
if (ref instanceof PsiReferenceExpression) {
|
||||
fixes.add(new SetInspectionOptionFix(this, "REPORT_CONSTANT_REFERENCE_VALUES",
|
||||
InspectionsBundle.message("inspection.data.flow.turn.off.constant.references.quickfix"),
|
||||
@@ -402,27 +400,23 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
|
||||
type = ProblemHighlightType.WEAK_WARNING;
|
||||
valueText = "Value";
|
||||
}
|
||||
holder.registerProblem(ref, MessageFormat.format("{0} <code>#ref</code> #loc is always ''{1}''", valueText, presentableName),
|
||||
type, fixes.toArray(LocalQuickFix.EMPTY_ARRAY));
|
||||
reporter.registerProblem(ref, MessageFormat.format("{0} <code>#ref</code> #loc is always ''{1}''", valueText, presentableName),
|
||||
type, fixes.toArray(LocalQuickFix.EMPTY_ARRAY));
|
||||
}
|
||||
|
||||
private static void reportPointlessSameArguments(ProblemsHolder holder,
|
||||
HashSet<PsiElement> reportedAnchors,
|
||||
DataFlowInstructionVisitor visitor) {
|
||||
private static void reportPointlessSameArguments(ProblemReporter reporter, DataFlowInstructionVisitor visitor) {
|
||||
visitor.pointlessSameArguments().forEach(expr -> {
|
||||
PsiElement name = expr.getReferenceNameElement();
|
||||
if (name != null && reportedAnchors.add(name)) {
|
||||
holder.registerProblem(name, InspectionsBundle.message("dataflow.message.pointless.same.arguments"));
|
||||
if (name != null) {
|
||||
reporter.registerProblem(name, InspectionsBundle.message("dataflow.message.pointless.same.arguments"));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void reportDuplicateAssignments(ProblemsHolder holder,
|
||||
HashSet<PsiElement> reportedAnchors,
|
||||
DataFlowInstructionVisitor visitor) {
|
||||
private void reportDuplicateAssignments(ProblemReporter reporter, DataFlowInstructionVisitor visitor) {
|
||||
visitor.sameValueAssignments().forEach(expr -> {
|
||||
expr = PsiUtil.skipParenthesizedExprDown(expr);
|
||||
if(expr == null || !reportedAnchors.add(expr)) return;
|
||||
if (expr == null) return;
|
||||
PsiAssignmentExpression assignment = PsiTreeUtil.getParentOfType(expr, PsiAssignmentExpression.class);
|
||||
PsiElement context = PsiTreeUtil.getParentOfType(expr, PsiForStatement.class, PsiClassInitializer.class);
|
||||
if (context instanceof PsiForStatement && PsiTreeUtil.isAncestor(((PsiForStatement)context).getInitialization(), expr, true)) {
|
||||
@@ -442,28 +436,21 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
|
||||
}
|
||||
}
|
||||
}
|
||||
holder.registerProblem(expr, InspectionsBundle.message("dataflow.message.redundant.assignment"), createRemoveAssignmentFix(assignment));
|
||||
reporter.registerProblem(expr, InspectionsBundle.message("dataflow.message.redundant.assignment"), createRemoveAssignmentFix(assignment));
|
||||
});
|
||||
}
|
||||
|
||||
private void reportMutabilityViolations(ProblemsHolder holder,
|
||||
Set<PsiElement> reportedAnchors,
|
||||
Set<PsiElement> violations,
|
||||
String message) {
|
||||
private void reportMutabilityViolations(ProblemsHolder holder, Set<PsiElement> violations, String message) {
|
||||
for (PsiElement violation : violations) {
|
||||
if (reportedAnchors.add(violation)) {
|
||||
holder.registerProblem(violation, message, createMutabilityViolationFix(holder, violation));
|
||||
}
|
||||
holder.registerProblem(violation, message, createMutabilityViolationFix(violation, holder.isOnTheFly()));
|
||||
}
|
||||
}
|
||||
|
||||
protected LocalQuickFix createMutabilityViolationFix(ProblemsHolder holder, PsiElement violation) {
|
||||
protected LocalQuickFix createMutabilityViolationFix(PsiElement violation, boolean onTheFly) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private void reportNullabilityProblems(ProblemsHolder holder,
|
||||
DataFlowInstructionVisitor visitor,
|
||||
HashSet<PsiElement> reportedAnchors) {
|
||||
private void reportNullabilityProblems(ProblemReporter reporter, DataFlowInstructionVisitor visitor) {
|
||||
Map<PsiExpression, ConstantResult> expressions = visitor.getConstantExpressions();
|
||||
visitor.problems().forEach(problem -> {
|
||||
if (NullabilityProblemKind.passingNullableArgumentToNonAnnotatedParameter.isMyProblem(problem) ||
|
||||
@@ -472,47 +459,44 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
|
||||
// these kinds are still reported separately
|
||||
return;
|
||||
}
|
||||
if (!reportedAnchors.add(problem.getAnchor())) return;
|
||||
if (problem.getAnchor() instanceof PsiParenthesizedExpression) {
|
||||
reportedAnchors.add(PsiUtil.skipParenthesizedExprDown((PsiExpression)problem.getAnchor()));
|
||||
}
|
||||
NullabilityProblemKind.innerClassNPE.ifMyProblem(problem, newExpression -> {
|
||||
List<LocalQuickFix> fixes = createNPEFixes(newExpression.getQualifier(), newExpression, holder.isOnTheFly());
|
||||
holder.registerProblem(getElementToHighlight(newExpression), problem.getMessage(expressions), fixes.toArray(LocalQuickFix.EMPTY_ARRAY));
|
||||
List<LocalQuickFix> fixes = createNPEFixes(newExpression.getQualifier(), newExpression, reporter.isOnTheFly());
|
||||
reporter.registerProblem(getElementToHighlight(newExpression), problem.getMessage(expressions), fixes.toArray(LocalQuickFix.EMPTY_ARRAY));
|
||||
});
|
||||
NullabilityProblemKind.callMethodRefNPE.ifMyProblem(problem, methodRef ->
|
||||
holder.registerProblem(methodRef, InspectionsBundle.message("dataflow.message.npe.methodref.invocation"),
|
||||
createMethodReferenceNPEFixes(methodRef, holder.isOnTheFly()).toArray(LocalQuickFix.EMPTY_ARRAY)));
|
||||
NullabilityProblemKind.callNPE.ifMyProblem(problem, call -> reportCallMayProduceNpe(holder, problem.getMessage(expressions), call));
|
||||
NullabilityProblemKind.passingNullableToNotNullParameter.ifMyProblem(problem, expr -> reportNullableArgument(holder, expr, expressions));
|
||||
reporter.registerProblem(methodRef, InspectionsBundle.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));
|
||||
NullabilityProblemKind.passingNullableToNotNullParameter.ifMyProblem(problem, expr -> reportNullableArgument(reporter, expr, expressions));
|
||||
NullabilityProblemKind.arrayAccessNPE.ifMyProblem(problem, expression -> {
|
||||
LocalQuickFix[] fix =
|
||||
createNPEFixes(expression.getArrayExpression(), expression, holder.isOnTheFly()).toArray(LocalQuickFix.EMPTY_ARRAY);
|
||||
holder.registerProblem(expression, problem.getMessage(expressions), fix);
|
||||
createNPEFixes(expression.getArrayExpression(), expression, reporter.isOnTheFly()).toArray(LocalQuickFix.EMPTY_ARRAY);
|
||||
reporter.registerProblem(expression, problem.getMessage(expressions), fix);
|
||||
});
|
||||
NullabilityProblemKind.fieldAccessNPE.ifMyProblem(problem, element -> {
|
||||
PsiElement parent = element.getParent();
|
||||
PsiExpression fieldAccess = parent instanceof PsiReferenceExpression ? (PsiExpression)parent : element;
|
||||
LocalQuickFix[] fix = createNPEFixes(element, fieldAccess, holder.isOnTheFly()).toArray(LocalQuickFix.EMPTY_ARRAY);
|
||||
holder.registerProblem(element, problem.getMessage(expressions), fix);
|
||||
LocalQuickFix[] fix = createNPEFixes(element, fieldAccess, reporter.isOnTheFly()).toArray(LocalQuickFix.EMPTY_ARRAY);
|
||||
reporter.registerProblem(element, problem.getMessage(expressions), fix);
|
||||
});
|
||||
NullabilityProblemKind.unboxingNullable.ifMyProblem(problem, element -> {
|
||||
if (element instanceof PsiTypeCastExpression && ((PsiTypeCastExpression)element).getType() instanceof PsiPrimitiveType) {
|
||||
element = Objects.requireNonNull(((PsiTypeCastExpression)element).getOperand());
|
||||
}
|
||||
holder.registerProblem(element, problem.getMessage(expressions));
|
||||
reporter.registerProblem(element, problem.getMessage(expressions));
|
||||
});
|
||||
NullabilityProblemKind.nullableFunctionReturn.ifMyProblem(problem, expr -> holder.registerProblem(expr, problem.getMessage(expressions)));
|
||||
NullabilityProblemKind.assigningToNotNull.ifMyProblem(problem, expr -> reportNullabilityProblem(holder, problem, expr, expressions));
|
||||
NullabilityProblemKind.storingToNotNullArray.ifMyProblem(problem, expr -> reportNullabilityProblem(holder, problem, expr, expressions));
|
||||
NullabilityProblemKind.nullableFunctionReturn.ifMyProblem(problem, expr -> reporter.registerProblem(expr, problem.getMessage(expressions)));
|
||||
NullabilityProblemKind.assigningToNotNull.ifMyProblem(problem, expr -> reportNullabilityProblem(reporter, problem, expr, expressions));
|
||||
NullabilityProblemKind.storingToNotNullArray.ifMyProblem(problem, expr -> reportNullabilityProblem(reporter, problem, expr, expressions));
|
||||
});
|
||||
}
|
||||
|
||||
private void reportNullabilityProblem(ProblemsHolder holder,
|
||||
private void reportNullabilityProblem(ProblemReporter reporter,
|
||||
NullabilityProblem<?> problem,
|
||||
PsiExpression expr,
|
||||
Map<PsiExpression, ConstantResult> expressions) {
|
||||
holder.registerProblem(expr, problem.getMessage(expressions), createNPEFixes(expr, expr, holder.isOnTheFly()).toArray(LocalQuickFix.EMPTY_ARRAY));
|
||||
LocalQuickFix[] fixes = createNPEFixes(expr, expr, reporter.isOnTheFly()).toArray(LocalQuickFix.EMPTY_ARRAY);
|
||||
reporter.registerProblem(expr, problem.getMessage(expressions), fixes);
|
||||
}
|
||||
|
||||
private static void reportArrayAccessProblems(ProblemsHolder holder, DataFlowInstructionVisitor visitor) {
|
||||
@@ -566,13 +550,10 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
|
||||
holder.registerProblem(annoName, msg, fixes);
|
||||
}
|
||||
|
||||
private static void reportAlwaysFailingCalls(ProblemsHolder holder,
|
||||
DataFlowInstructionVisitor visitor,
|
||||
HashSet<PsiElement> reportedAnchors) {
|
||||
private static void reportAlwaysFailingCalls(ProblemReporter reporter, DataFlowInstructionVisitor visitor) {
|
||||
visitor.alwaysFailingCalls().remove(TestUtils::isExceptionExpected).forEach(call -> {
|
||||
if (reportedAnchors.add(call)) {
|
||||
holder.registerProblem(getElementToHighlight(call), getContractMessage(JavaMethodContractUtil.getMethodCallContracts(call)));
|
||||
}
|
||||
String message = getContractMessage(JavaMethodContractUtil.getMethodCallContracts(call));
|
||||
reporter.registerProblem(getElementToHighlight(call), message);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -602,31 +583,27 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
|
||||
return call;
|
||||
}
|
||||
|
||||
private static void reportOptionalOfNullableImprovements(ProblemsHolder holder,
|
||||
Set<PsiElement> reportedAnchors,
|
||||
Map<PsiElement, ThreeState> nullArgs) {
|
||||
private static void reportOptionalOfNullableImprovements(ProblemReporter reporter, Map<PsiElement, ThreeState> nullArgs) {
|
||||
nullArgs.forEach((anchor, alwaysPresent) -> {
|
||||
if (alwaysPresent == ThreeState.UNSURE) return;
|
||||
if (reportedAnchors.add(anchor)) {
|
||||
if (alwaysPresent.toBoolean()) {
|
||||
holder.registerProblem(anchor, "Passing a non-null argument to <code>Optional</code>",
|
||||
if (alwaysPresent.toBoolean()) {
|
||||
reporter.registerProblem(anchor, "Passing a non-null argument to <code>Optional</code>",
|
||||
DfaOptionalSupport.createReplaceOptionalOfNullableWithOfFix(anchor));
|
||||
} else {
|
||||
holder.registerProblem(anchor, "Passing <code>null</code> argument to <code>Optional</code>",
|
||||
}
|
||||
else {
|
||||
reporter.registerProblem(anchor, "Passing <code>null</code> argument to <code>Optional</code>",
|
||||
DfaOptionalSupport.createReplaceOptionalOfNullableWithEmptyFix(anchor));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void reportNullableArgumentsPassedToNonAnnotated(DataFlowInstructionVisitor visitor, ProblemsHolder holder, Set<PsiElement> reportedAnchors) {
|
||||
private void reportNullableArgumentsPassedToNonAnnotated(DataFlowInstructionVisitor visitor, ProblemReporter reporter) {
|
||||
for (PsiElement anchor : visitor.problems()
|
||||
.map(NullabilityProblemKind.passingNullableArgumentToNonAnnotatedParameter::asMyProblem).nonNull()
|
||||
.map(NullabilityProblem::getAnchor)) {
|
||||
if (reportedAnchors.contains(anchor)) continue;
|
||||
|
||||
if (anchor.getParent() instanceof PsiMethodReferenceExpression) {
|
||||
holder.registerProblem(anchor.getParent(), "Method reference argument might be null but passed to non-annotated parameter");
|
||||
reporter.registerProblem(anchor.getParent(), "Method reference argument might be null but passed to non-annotated parameter");
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -634,7 +611,7 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
|
||||
final String text = isNullLiteralExpression(expression) || visitor.getConstantExpressions().get(expression) == ConstantResult.NULL
|
||||
? "Passing <code>null</code> argument to non-annotated parameter"
|
||||
: "Argument <code>#ref</code> #loc might be null but passed to non-annotated parameter";
|
||||
List<LocalQuickFix> fixes = createNPEFixes(expression, expression, holder.isOnTheFly());
|
||||
List<LocalQuickFix> fixes = createNPEFixes(expression, expression, reporter.isOnTheFly());
|
||||
final PsiElement parent = anchor.getParent();
|
||||
if (parent instanceof PsiExpressionList) {
|
||||
final int idx = ArrayUtilRt.find(((PsiExpressionList)parent).getExpressions(), anchor);
|
||||
@@ -646,35 +623,30 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
|
||||
final PsiParameter[] parameters = psiMethod.getParameterList().getParameters();
|
||||
if (idx < parameters.length) {
|
||||
fixes.add(AddAnnotationPsiFix.createAddNullableFix(parameters[idx]));
|
||||
holder.registerProblem(anchor, text, fixes.toArray(LocalQuickFix.EMPTY_ARRAY));
|
||||
reportedAnchors.add(anchor);
|
||||
reportedAnchors.add(expression);
|
||||
reporter.registerProblem(anchor, text, fixes.toArray(LocalQuickFix.EMPTY_ARRAY));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void reportNullableAssignedToNonAnnotatedFields(DataFlowInstructionVisitor visitor, ProblemsHolder holder, Set<PsiElement> reportedAnchors) {
|
||||
|
||||
private void reportNullableAssignedToNonAnnotatedFields(DataFlowInstructionVisitor visitor, ProblemReporter reporter) {
|
||||
for (PsiElement anchor : visitor.problems()
|
||||
.map(NullabilityProblemKind.assigningNullableValueToNonAnnotatedField::asMyProblem).nonNull()
|
||||
.map(NullabilityProblem::getAnchor)) {
|
||||
if (reportedAnchors.contains(anchor)) continue;
|
||||
|
||||
PsiExpression expression = (PsiExpression)anchor;
|
||||
String text = isNullLiteralExpression(expression) ||
|
||||
visitor.getConstantExpressions().get(expression) == ConstantResult.NULL
|
||||
? "Assigning <code>null</code> value to non-annotated field"
|
||||
: "Expression <code>#ref</code> #loc might be null but is assigned to non-annotated field";
|
||||
List<LocalQuickFix> fixes = createNPEFixes(expression, expression, holder.isOnTheFly());
|
||||
List<LocalQuickFix> fixes = createNPEFixes(expression, expression, reporter.isOnTheFly());
|
||||
PsiField field = getAssignedField(anchor);
|
||||
if (field != null) {
|
||||
fixes.add(AddAnnotationPsiFix.createAddNullableFix(field));
|
||||
holder.registerProblem(anchor, text, fixes.toArray(LocalQuickFix.EMPTY_ARRAY));
|
||||
reportedAnchors.add(anchor);
|
||||
reporter.registerProblem(anchor, text, fixes.toArray(LocalQuickFix.EMPTY_ARRAY));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -690,44 +662,39 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
|
||||
return null;
|
||||
}
|
||||
|
||||
private void reportCallMayProduceNpe(ProblemsHolder holder,
|
||||
String message,
|
||||
PsiMethodCallExpression callExpression) {
|
||||
private void reportCallMayProduceNpe(ProblemReporter reporter, String message, PsiMethodCallExpression callExpression) {
|
||||
PsiReferenceExpression methodExpression = callExpression.getMethodExpression();
|
||||
List<LocalQuickFix> fixes = createNPEFixes(methodExpression.getQualifierExpression(), callExpression, holder.isOnTheFly());
|
||||
List<LocalQuickFix> fixes = createNPEFixes(methodExpression.getQualifierExpression(), callExpression, reporter.isOnTheFly());
|
||||
ContainerUtil.addIfNotNull(fixes, ReplaceWithObjectsEqualsFix.createFix(callExpression, methodExpression));
|
||||
|
||||
PsiElement toHighlight = getElementToHighlight(callExpression);
|
||||
holder.registerProblem(toHighlight, message, fixes.toArray(LocalQuickFix.EMPTY_ARRAY));
|
||||
reporter.registerProblem(toHighlight, message, fixes.toArray(LocalQuickFix.EMPTY_ARRAY));
|
||||
}
|
||||
|
||||
private static void reportFailingCasts(ProblemsHolder holder, DataFlowInstructionVisitor visitor, HashSet<PsiElement> reportedAnchors) {
|
||||
private static void reportFailingCasts(ProblemReporter reporter, DataFlowInstructionVisitor visitor) {
|
||||
for (TypeCastInstruction instruction : visitor.getClassCastExceptionInstructions()) {
|
||||
if (reportedAnchors.add(instruction.getExpression().getCastType())) {
|
||||
PsiTypeCastExpression typeCast = instruction.getExpression();
|
||||
PsiExpression operand = typeCast.getOperand();
|
||||
PsiTypeElement castType = typeCast.getCastType();
|
||||
assert castType != null;
|
||||
assert operand != null;
|
||||
holder.registerProblem(castType, InspectionsBundle.message("dataflow.message.cce", operand.getText()));
|
||||
}
|
||||
PsiTypeCastExpression typeCast = instruction.getExpression();
|
||||
PsiExpression operand = typeCast.getOperand();
|
||||
PsiTypeElement castType = typeCast.getCastType();
|
||||
assert castType != null;
|
||||
assert operand != null;
|
||||
reporter.registerProblem(castType, InspectionsBundle.message("dataflow.message.cce", operand.getText()));
|
||||
}
|
||||
}
|
||||
|
||||
private void handleBranchingInstruction(ProblemsHolder holder,
|
||||
private void handleBranchingInstruction(ProblemReporter reporter,
|
||||
StandardInstructionVisitor visitor,
|
||||
Set<Instruction> trueSet,
|
||||
HashSet<PsiElement> reportedAnchors,
|
||||
BranchingInstruction instruction) {
|
||||
PsiElement psiAnchor = instruction.getPsiAnchor();
|
||||
if (instruction instanceof InstanceofInstruction && visitor.isInstanceofRedundant((InstanceofInstruction)instruction)) {
|
||||
if (visitor.canBeNull((InstanceofInstruction)instruction)) {
|
||||
holder.registerProblem(psiAnchor,
|
||||
InspectionsBundle.message("dataflow.message.redundant.instanceof"),
|
||||
new RedundantInstanceofFix());
|
||||
reporter.registerProblem(psiAnchor,
|
||||
InspectionsBundle.message("dataflow.message.redundant.instanceof"),
|
||||
new RedundantInstanceofFix());
|
||||
}
|
||||
else {
|
||||
reportConstantBoolean(holder, psiAnchor, reportedAnchors, true);
|
||||
reportConstantBoolean(reporter, psiAnchor, true);
|
||||
}
|
||||
}
|
||||
else if (psiAnchor != null &&
|
||||
@@ -735,13 +702,11 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
|
||||
!isFlagCheck(psiAnchor)) {
|
||||
boolean evaluatesToTrue = trueSet.contains(instruction);
|
||||
final PsiElement parent = psiAnchor.getParent();
|
||||
if (parent instanceof PsiAssignmentExpression &&
|
||||
((PsiAssignmentExpression)parent).getLExpression() == psiAnchor &&
|
||||
reportedAnchors.add(psiAnchor)) {
|
||||
holder.registerProblem(
|
||||
if (parent instanceof PsiAssignmentExpression && ((PsiAssignmentExpression)parent).getLExpression() == psiAnchor) {
|
||||
reporter.registerProblem(
|
||||
psiAnchor,
|
||||
InspectionsBundle.message("dataflow.message.pointless.assignment.expression", Boolean.toString(evaluatesToTrue)),
|
||||
createConditionalAssignmentFixes(evaluatesToTrue, (PsiAssignmentExpression)parent, holder.isOnTheFly())
|
||||
createConditionalAssignmentFixes(evaluatesToTrue, (PsiAssignmentExpression)parent, reporter.isOnTheFly())
|
||||
);
|
||||
}
|
||||
else {
|
||||
@@ -750,43 +715,39 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
|
||||
if (range != null) {
|
||||
// report rare cases like a == b == c where "a == b" part is constant
|
||||
String message = InspectionsBundle.message("dataflow.message.constant.condition", Boolean.toString(evaluatesToTrue));
|
||||
holder.registerProblem(psiAnchor, range, message);
|
||||
reporter.registerProblem(psiAnchor, range, message);
|
||||
// do not add to reported anchors if only part of expression was reported
|
||||
}
|
||||
else if (PsiUtil.skipParenthesizedExprUp(psiAnchor.getParent()) instanceof PsiForeachStatement) {
|
||||
// highlighted for-each iterated value means evaluatesToTrue == "collection is always empty"
|
||||
if (!evaluatesToTrue || !reportedAnchors.add(psiAnchor)) {
|
||||
if (!evaluatesToTrue) {
|
||||
// loop on always non-empty collection -- nothing to report
|
||||
return;
|
||||
}
|
||||
boolean array = psiAnchor instanceof PsiExpression && ((PsiExpression)psiAnchor).getType() instanceof PsiArrayType;
|
||||
holder.registerProblem(psiAnchor, array ?
|
||||
InspectionsBundle.message("dataflow.message.loop.on.empty.array") :
|
||||
InspectionsBundle.message("dataflow.message.loop.on.empty.collection"));
|
||||
reporter.registerProblem(psiAnchor, array ?
|
||||
InspectionsBundle.message("dataflow.message.loop.on.empty.array") :
|
||||
InspectionsBundle.message("dataflow.message.loop.on.empty.collection"));
|
||||
}
|
||||
else if (!(psiAnchor instanceof PsiMethodReferenceExpression)) {
|
||||
reportConstantBoolean(holder, psiAnchor, reportedAnchors, evaluatesToTrue);
|
||||
reportConstantBoolean(reporter, psiAnchor, evaluatesToTrue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void reportConstantBoolean(ProblemsHolder holder,
|
||||
PsiElement psiAnchor,
|
||||
HashSet<PsiElement> reportedAnchors,
|
||||
boolean evaluatesToTrue) {
|
||||
private void reportConstantBoolean(ProblemReporter reporter, PsiElement psiAnchor, boolean evaluatesToTrue) {
|
||||
while (psiAnchor instanceof PsiParenthesizedExpression) {
|
||||
psiAnchor = ((PsiParenthesizedExpression)psiAnchor).getExpression();
|
||||
}
|
||||
if (psiAnchor == null || shouldBeSuppressed(psiAnchor)) return;
|
||||
boolean isAssertion = isAssertionEffectively(psiAnchor, evaluatesToTrue);
|
||||
if (DONT_REPORT_TRUE_ASSERT_STATEMENTS && isAssertion) return;
|
||||
if (!reportedAnchors.add(psiAnchor)) return;
|
||||
|
||||
List<LocalQuickFix> fixes = new ArrayList<>();
|
||||
if (!isCoveredBySurroundingFix(psiAnchor, evaluatesToTrue)) {
|
||||
ContainerUtil.addIfNotNull(fixes, createSimplifyBooleanExpressionFix(psiAnchor, evaluatesToTrue));
|
||||
if (isAssertion && holder.isOnTheFly()) {
|
||||
if (isAssertion && reporter.isOnTheFly()) {
|
||||
fixes.add(new SetInspectionOptionFix(this, "DONT_REPORT_TRUE_ASSERT_STATEMENTS",
|
||||
InspectionsBundle.message("inspection.data.flow.turn.off.true.asserts.quickfix"), true));
|
||||
}
|
||||
@@ -795,7 +756,7 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
|
||||
String message = InspectionsBundle.message(isAtRHSOfBooleanAnd(psiAnchor) ?
|
||||
"dataflow.message.constant.condition.when.reached" :
|
||||
"dataflow.message.constant.condition", Boolean.toString(evaluatesToTrue));
|
||||
holder.registerProblem(psiAnchor, message, fixes.toArray(LocalQuickFix.EMPTY_ARRAY));
|
||||
reporter.registerProblem(psiAnchor, message, fixes.toArray(LocalQuickFix.EMPTY_ARRAY));
|
||||
}
|
||||
|
||||
private static boolean isCoveredBySurroundingFix(PsiElement anchor, boolean evaluatesToTrue) {
|
||||
@@ -875,21 +836,21 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
|
||||
return LocalQuickFix.EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
private void reportNullableArgument(ProblemsHolder holder,
|
||||
private void reportNullableArgument(ProblemReporter reporter,
|
||||
PsiElement anchor,
|
||||
Map<PsiExpression, ConstantResult> expressions) {
|
||||
if (anchor.getParent() instanceof PsiMethodReferenceExpression) {
|
||||
PsiMethodReferenceExpression methodRef = (PsiMethodReferenceExpression)anchor.getParent();
|
||||
holder.registerProblem(methodRef, InspectionsBundle.message("dataflow.message.passing.nullable.argument.methodref"),
|
||||
createMethodReferenceNPEFixes(methodRef, holder.isOnTheFly()).toArray(LocalQuickFix.EMPTY_ARRAY));
|
||||
reporter.registerProblem(methodRef, InspectionsBundle.message("dataflow.message.passing.nullable.argument.methodref"),
|
||||
createMethodReferenceNPEFixes(methodRef, reporter.isOnTheFly()).toArray(LocalQuickFix.EMPTY_ARRAY));
|
||||
}
|
||||
else {
|
||||
PsiExpression expression = PsiUtil.skipParenthesizedExprDown((PsiExpression)anchor);
|
||||
final String text = isNullLiteralExpression(expression) || expressions.get(expression) == ConstantResult.NULL
|
||||
? InspectionsBundle.message("dataflow.message.passing.null.argument")
|
||||
: InspectionsBundle.message("dataflow.message.passing.nullable.argument");
|
||||
List<LocalQuickFix> fixes = createNPEFixes(expression, expression, holder.isOnTheFly());
|
||||
holder.registerProblem(anchor, text, fixes.toArray(LocalQuickFix.EMPTY_ARRAY));
|
||||
List<LocalQuickFix> fixes = createNPEFixes(expression, expression, reporter.isOnTheFly());
|
||||
reporter.registerProblem(anchor, text, fixes.toArray(LocalQuickFix.EMPTY_ARRAY));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -901,13 +862,10 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
|
||||
return null;
|
||||
}
|
||||
|
||||
private void reportNullableReturns(DataFlowInstructionVisitor visitor,
|
||||
ProblemsHolder holder,
|
||||
Set<PsiElement> reportedAnchors,
|
||||
@NotNull PsiElement block) {
|
||||
private void reportNullableReturns(DataFlowInstructionVisitor visitor, ProblemReporter reporter, @NotNull PsiElement block) {
|
||||
final PsiMethod method = getScopeMethod(block);
|
||||
if (method == null) return;
|
||||
NullableNotNullManager manager = NullableNotNullManager.getInstance(holder.getProject());
|
||||
NullableNotNullManager manager = NullableNotNullManager.getInstance(method.getProject());
|
||||
NullabilityAnnotationInfo info = manager.findEffectiveNullabilityInfo(method);
|
||||
PsiAnnotation anno = info == null ? null : info.getAnnotation();
|
||||
Nullability nullability = info == null ? Nullability.UNKNOWN : info.getNullability();
|
||||
@@ -927,16 +885,14 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
|
||||
|
||||
for (NullabilityProblem<PsiExpression> problem : visitor.problems().map(NullabilityProblemKind.nullableReturn::asMyProblem).nonNull()) {
|
||||
final PsiExpression anchor = problem.getAnchor();
|
||||
if (!reportedAnchors.add(anchor)) continue;
|
||||
PsiExpression expr = PsiUtil.skipParenthesizedExprDown(anchor);
|
||||
reportedAnchors.add(expr);
|
||||
|
||||
if (nullability == Nullability.NOT_NULL) {
|
||||
String presentable = NullableStuffInspectionBase.getPresentableAnnoName(anno);
|
||||
final String text = isNullLiteralExpression(expr) || visitor.getConstantExpressions().get(expr) == ConstantResult.NULL
|
||||
? InspectionsBundle.message("dataflow.message.return.null.from.notnull", presentable)
|
||||
: InspectionsBundle.message("dataflow.message.return.nullable.from.notnull", presentable);
|
||||
holder.registerProblem(anchor, text);
|
||||
reporter.registerProblem(anchor, text);
|
||||
}
|
||||
else if (AnnotationUtil.isAnnotatingApplicable(anchor)) {
|
||||
final String defaultNullable = manager.getDefaultNullable();
|
||||
@@ -948,7 +904,7 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
|
||||
PsiTreeUtil.getParentOfType(anchor, PsiMethod.class, PsiLambdaExpression.class) instanceof PsiLambdaExpression
|
||||
? LocalQuickFix.EMPTY_ARRAY
|
||||
: new LocalQuickFix[]{ new AnnotateMethodFix(defaultNullable, ArrayUtil.toStringArray(manager.getNotNulls()))};
|
||||
holder.registerProblem(anchor, text, fixes);
|
||||
reporter.registerProblem(anchor, text, fixes);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1159,4 +1115,52 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
|
||||
public String getShortName() {
|
||||
return SHORT_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ProblemsHolder} wrapper to avoid reporting two problems on the same anchor
|
||||
*/
|
||||
static class ProblemReporter {
|
||||
private final Set<PsiElement> myReportedAnchors = new HashSet<>();
|
||||
private final ProblemsHolder myHolder;
|
||||
|
||||
ProblemReporter(ProblemsHolder holder) {
|
||||
myHolder = holder;
|
||||
}
|
||||
|
||||
void registerProblem(PsiElement element, String message, LocalQuickFix... fixes) {
|
||||
if (register(element)) {
|
||||
myHolder.registerProblem(element, message, fixes);
|
||||
}
|
||||
}
|
||||
|
||||
void registerProblem(PsiElement element, String message, ProblemHighlightType type, LocalQuickFix... fixes) {
|
||||
if (register(element)) {
|
||||
myHolder.registerProblem(element, message, type, fixes);
|
||||
}
|
||||
}
|
||||
|
||||
void registerProblem(PsiElement element, TextRange range, String message, LocalQuickFix... fixes) {
|
||||
if (range == null) {
|
||||
registerProblem(element, message, fixes);
|
||||
}
|
||||
else {
|
||||
myHolder.registerProblem(element, range, message, fixes);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean register(PsiElement element) {
|
||||
if (!myReportedAnchors.add(element)) return false;
|
||||
if (element instanceof PsiParenthesizedExpression) {
|
||||
PsiExpression deparenthesized = PsiUtil.skipParenthesizedExprDown((PsiExpression)element);
|
||||
if (deparenthesized != null) {
|
||||
myReportedAnchors.add(deparenthesized);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean isOnTheFly() {
|
||||
return myHolder.isOnTheFly();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,8 +72,8 @@ public class DataFlowInspection extends DataFlowInspectionBase {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LocalQuickFix createMutabilityViolationFix(ProblemsHolder holder, PsiElement violation) {
|
||||
return WrapWithMutableCollectionFix.createFix(violation, holder.isOnTheFly());
|
||||
protected LocalQuickFix createMutabilityViolationFix(PsiElement violation, boolean onTheFly) {
|
||||
return WrapWithMutableCollectionFix.createFix(violation, onTheFly);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
Reference in New Issue
Block a user