diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java index 2840488f621b..62bebd95e75f 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java @@ -235,7 +235,10 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec return Collections.emptyList(); } - protected @NotNull List createNPEFixes(@Nullable PsiExpression qualifier, PsiExpression expression, boolean onTheFly) { + protected @NotNull List createNPEFixes(@Nullable PsiExpression qualifier, + PsiExpression expression, + boolean onTheFly, + boolean alwaysNull) { return Collections.emptyList(); } @@ -613,17 +616,19 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec } // 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); NullabilityProblemKind.innerClassNPE.ifMyProblem(problem, newExpression -> { - List fixes = createNPEFixes(newExpression.getQualifier(), newExpression, reporter.isOnTheFly()); + List fixes = createNPEFixes(newExpression.getQualifier(), newExpression, reporter.isOnTheFly(), alwaysNull); reporter .registerProblem(getElementToHighlight(newExpression), problem.getMessage(expressions), 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)); + NullabilityProblemKind.callNPE.ifMyProblem(problem, call -> + reportCallMayProduceNpe(reporter, problem.getMessage(expressions), call, alwaysNull)); NullabilityProblemKind.passingToNotNullParameter.ifMyProblem(problem, expr -> { - List fixes = createNPEFixes(expression, expression, reporter.isOnTheFly()); + List fixes = createNPEFixes(expression, expression, reporter.isOnTheFly(), alwaysNull); reporter.registerProblem(expression, problem.getMessage(expressions), fixes.toArray(LocalQuickFix.EMPTY_ARRAY)); }); NullabilityProblemKind.passingToNotNullMethodRefParameter.ifMyProblem(problem, methodRef -> { @@ -635,14 +640,14 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec reporter.registerProblem(methodRef, JavaAnalysisBundle.message("dataflow.message.unboxing.nullable.argument.methodref"), fixes); }); NullabilityProblemKind.arrayAccessNPE.ifMyProblem(problem, arrayAccess -> { - LocalQuickFix[] fixes = - createNPEFixes(arrayAccess.getArrayExpression(), arrayAccess, reporter.isOnTheFly()).toArray(LocalQuickFix.EMPTY_ARRAY); + LocalQuickFix[] fixes = createNPEFixes(arrayAccess.getArrayExpression(), arrayAccess, reporter.isOnTheFly(), + alwaysNull).toArray(LocalQuickFix.EMPTY_ARRAY); reporter.registerProblem(arrayAccess, problem.getMessage(expressions), 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()).toArray(LocalQuickFix.EMPTY_ARRAY); + LocalQuickFix[] fix = createNPEFixes(element, fieldAccess, reporter.isOnTheFly(), alwaysNull).toArray(LocalQuickFix.EMPTY_ARRAY); reporter.registerProblem(element, problem.getMessage(expressions), fix); }); NullabilityProblemKind.unboxingNullable.ifMyProblem(problem, element -> { @@ -664,9 +669,9 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec NullabilityProblemKind.passingToNonAnnotatedMethodRefParameter.ifMyProblem( problem, methodRef -> reportNullableArgumentPassedToNonAnnotatedMethodRef(reporter, expressions, problem, methodRef)); NullabilityProblemKind.passingToNonAnnotatedParameter.ifMyProblem( - problem, top -> reportNullableArgumentsPassedToNonAnnotated(reporter, problem.getMessage(expressions), expression, top)); + problem, top -> reportNullableArgumentsPassedToNonAnnotated(reporter, problem.getMessage(expressions), expression, top, alwaysNull)); NullabilityProblemKind.assigningToNonAnnotatedField.ifMyProblem( - problem, top -> reportNullableAssignedToNonAnnotatedField(reporter, top, expression, problem.getMessage(expressions))); + problem, top -> reportNullableAssignedToNonAnnotatedField(reporter, top, expression, problem.getMessage(expressions), alwaysNull)); } } } @@ -675,7 +680,8 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec NullabilityProblem problem, PsiExpression expr, Map expressions) { - LocalQuickFix[] fixes = createNPEFixes(expr, expr, reporter.isOnTheFly()).toArray(LocalQuickFix.EMPTY_ARRAY); + LocalQuickFix[] fixes = createNPEFixes(expr, expr, reporter.isOnTheFly(), problem.isAlwaysNull(expressions)) + .toArray(LocalQuickFix.EMPTY_ARRAY); reporter.registerProblem(expr, problem.getMessage(expressions), fixes); } @@ -791,10 +797,10 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec private void reportNullableArgumentsPassedToNonAnnotated(ProblemReporter reporter, @InspectionMessage String message, PsiExpression expression, - PsiExpression top) { + PsiExpression top, boolean alwaysNull) { PsiParameter parameter = MethodCallUtils.getParameterForArgument(top); if (parameter != null && BaseIntentionAction.canModify(parameter) && AnnotationUtil.isAnnotatingApplicable(parameter)) { - List fixes = createNPEFixes(expression, top, reporter.isOnTheFly()); + List fixes = createNPEFixes(expression, top, reporter.isOnTheFly(), alwaysNull); fixes.add(AddAnnotationPsiFix.createAddNullableFix(parameter)); reporter.registerProblem(expression, message, fixes.toArray(LocalQuickFix.EMPTY_ARRAY)); } @@ -803,10 +809,11 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec private void reportNullableAssignedToNonAnnotatedField(ProblemReporter reporter, PsiExpression top, PsiExpression expression, - @InspectionMessage String message) { + @InspectionMessage String message, + boolean alwaysNull) { PsiField field = getAssignedField(top); if (field != null) { - List fixes = createNPEFixes(expression, top, reporter.isOnTheFly()); + List fixes = createNPEFixes(expression, top, reporter.isOnTheFly(), alwaysNull); fixes.add(AddAnnotationPsiFix.createAddNullableFix(field)); reporter.registerProblem(expression, message, fixes.toArray(LocalQuickFix.EMPTY_ARRAY)); } @@ -822,10 +829,13 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec return null; } - private void reportCallMayProduceNpe(ProblemReporter reporter, @InspectionMessage String message, PsiMethodCallExpression callExpression) { + private void reportCallMayProduceNpe(ProblemReporter reporter, @InspectionMessage String message, PsiMethodCallExpression callExpression, + boolean alwaysNull) { PsiReferenceExpression methodExpression = callExpression.getMethodExpression(); - List fixes = createNPEFixes(methodExpression.getQualifierExpression(), callExpression, reporter.isOnTheFly()); - ContainerUtil.addIfNotNull(fixes, ReplaceWithObjectsEqualsFix.createFix(callExpression, methodExpression)); + List fixes = createNPEFixes(methodExpression.getQualifierExpression(), callExpression, reporter.isOnTheFly(), alwaysNull); + if (!alwaysNull) { + ContainerUtil.addIfNotNull(fixes, ReplaceWithObjectsEqualsFix.createFix(callExpression, methodExpression)); + } PsiElement toHighlight = getElementToHighlight(callExpression); reporter.registerProblem(toHighlight, message, fixes.toArray(LocalQuickFix.EMPTY_ARRAY)); @@ -1051,14 +1061,14 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec final PsiExpression anchor = problem.getAnchor(); PsiExpression expr = problem.getDereferencedExpression(); - boolean exactlyNull = isNullLiteralExpression(expr) || expressions.get(expr) == ConstantResult.NULL; + boolean exactlyNull = problem.isAlwaysNull(expressions); if (!REPORT_UNSOUND_WARNINGS && !exactlyNull) continue; if (nullability == Nullability.NOT_NULL) { String presentable = NullableStuffInspectionBase.getPresentableAnnoName(anno); final String text = exactlyNull ? JavaAnalysisBundle.message("dataflow.message.return.null.from.notnull", presentable) : JavaAnalysisBundle.message("dataflow.message.return.nullable.from.notnull", presentable); - reporter.registerProblem(expr, text, createNPEFixes(expr, expr, reporter.isOnTheFly()).toArray(LocalQuickFix.EMPTY_ARRAY)); + reporter.registerProblem(expr, text, createNPEFixes(expr, expr, reporter.isOnTheFly(), exactlyNull).toArray(LocalQuickFix.EMPTY_ARRAY)); } else if (AnnotationUtil.isAnnotatingApplicable(anchor)) { final String defaultNullable = manager.getDefaultNullable(); @@ -1222,10 +1232,6 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec return field.hasInitializer() && PsiUtil.isConstantExpression(field.getInitializer()); } - private static boolean isNullLiteralExpression(PsiElement expr) { - return expr instanceof PsiExpression && ExpressionUtils.isNullLiteral((PsiExpression)expr); - } - private @Nullable LocalQuickFix createSimplifyBooleanExpressionFix(PsiElement element, final boolean value) { LocalQuickFixOnPsiElement fix = createSimplifyBooleanFix(element, value); if (fix == null) return null; diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/NullabilityProblemKind.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/NullabilityProblemKind.java index 03745157c9ce..e4d61a2ecb42 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/NullabilityProblemKind.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/NullabilityProblemKind.java @@ -575,19 +575,20 @@ public final class NullabilityProblemKind { return myFromUnknown; } + public boolean isAlwaysNull(@NotNull Map expressions) { + PsiExpression expression = PsiUtil.skipParenthesizedExprDown(getDereferencedExpression()); + return expression != null && + (ExpressionUtils.isNullLiteral(expression) || expressions.get(expression) == DataFlowInspectionBase.ConstantResult.NULL); + } + @NotNull - public @InspectionMessage String getMessage(Map expressions) { + public @InspectionMessage String getMessage(@NotNull Map expressions) { 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") : ""; - PsiExpression expression = PsiUtil.skipParenthesizedExprDown(getDereferencedExpression()); - if (expression != null) { - if (ExpressionUtils.isNullLiteral(expression) || expressions.get(expression) == DataFlowInspectionBase.ConstantResult.NULL) { - return myKind.myAlwaysNullMessage.get() + suffix; - } - } - return myKind.myNormalMessage.get() + suffix; + Supplier<@Nls String> msg = isAlwaysNull(expressions) ? myKind.myAlwaysNullMessage : myKind.myNormalMessage; + return msg.get() + suffix; } @NotNull diff --git a/java/java-impl-inspections/src/com/intellij/codeInspection/dataFlow/DataFlowInspection.java b/java/java-impl-inspections/src/com/intellij/codeInspection/dataFlow/DataFlowInspection.java index 222e4ba3f0f1..34b183ce75a8 100644 --- a/java/java-impl-inspections/src/com/intellij/codeInspection/dataFlow/DataFlowInspection.java +++ b/java/java-impl-inspections/src/com/intellij/codeInspection/dataFlow/DataFlowInspection.java @@ -170,7 +170,10 @@ public class DataFlowInspection extends DataFlowInspectionBase { @Override @NotNull - protected List createNPEFixes(@Nullable PsiExpression qualifier, PsiExpression expression, boolean onTheFly) { + protected List createNPEFixes(@Nullable PsiExpression qualifier, + PsiExpression expression, + boolean onTheFly, + boolean alwaysNull) { qualifier = PsiUtil.deparenthesizeExpression(qualifier); final List fixes = new SmartList<>(); @@ -182,7 +185,7 @@ public class DataFlowInspection extends DataFlowInspectionBase { if (isVolatileFieldReference(qualifier)) { ContainerUtil.addIfNotNull(fixes, createIntroduceVariableFix()); } - else if (!ExpressionUtils.isNullLiteral(qualifier) && !SideEffectChecker.mayHaveSideEffects(qualifier)) { + else if (!alwaysNull && !SideEffectChecker.mayHaveSideEffects(qualifier)) { String suffix = " != null"; if (PsiUtil.getLanguageLevel(qualifier).isAtLeast(LanguageLevel.JDK_1_4) && CodeBlockSurrounder.canSurround(expression)) { String replacement = ParenthesesUtils.getText(qualifier, ParenthesesUtils.EQUALITY_PRECEDENCE) + suffix; @@ -198,7 +201,7 @@ public class DataFlowInspection extends DataFlowInspectionBase { } } - if (!ExpressionUtils.isNullLiteral(qualifier) && PsiUtil.isLanguageLevel7OrHigher(qualifier)) { + if (!alwaysNull && PsiUtil.isLanguageLevel7OrHigher(qualifier)) { fixes.add(new SurroundWithRequireNonNullFix(qualifier)); } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addAssert/afterFieldInitializer.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addAssert/afterFieldInitializer.java index f0efd8080bdb..e21f499dda2e 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addAssert/afterFieldInitializer.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addAssert/afterFieldInitializer.java @@ -1,6 +1,6 @@ // "Assert 'myFoo != null'" "true" class A{ - private final String myFoo = null; + private final String myFoo = Math.random() > 0.5 ? "" : null; String myBar; { diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addAssert/afterLambda.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addAssert/afterLambda.java index d2fbdf739f41..26490b26a7ab 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addAssert/afterLambda.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addAssert/afterLambda.java @@ -3,9 +3,9 @@ import java.util.function.Supplier; class A{ void test(){ - Object container = null; + Object container = Math.random() > 0.5 ? "" : null; Supplier r = () -> { - if (container == null) { + if (Math.random() > 0.5) { assert container != null; return container.toString(); } else { diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addAssert/beforeFieldInitializer.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addAssert/beforeFieldInitializer.java index 2957a9542b72..d84ac10a562b 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addAssert/beforeFieldInitializer.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addAssert/beforeFieldInitializer.java @@ -1,5 +1,5 @@ // "Assert 'myFoo != null'" "true" class A{ - private final String myFoo = null; + private final String myFoo = Math.random() > 0.5 ? "" : null; String myBar = myFoo.substring(0); } \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addAssert/beforeLambda.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addAssert/beforeLambda.java index 501ed1d8de89..84c9f2a46247 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addAssert/beforeLambda.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addAssert/beforeLambda.java @@ -3,7 +3,7 @@ import java.util.function.Supplier; class A{ void test(){ - Object container = null; - Supplier r = () -> container == null ? container.toString() : ""; + Object container = Math.random() > 0.5 ? "" : null; + Supplier r = () -> Math.random() > 0.5 ? container.toString() : ""; } } \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceWithTernaryOperator/afterMethodCall.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceWithTernaryOperator/afterMethodCall.java index 1493917d577e..4bd0fd7fe7b9 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceWithTernaryOperator/afterMethodCall.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceWithTernaryOperator/afterMethodCall.java @@ -4,7 +4,7 @@ import org.jetbrains.annotations.NotNull; class A{ void test(@NotNull List l) { - final List list = null; + final List list = Math.random() > 0.5 ? new List() : null; test(list != null ? list : null); } } \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceWithTernaryOperator/afterSimple.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceWithTernaryOperator/afterSimple.java index dcfba17b5667..3b736ab27019 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceWithTernaryOperator/afterSimple.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceWithTernaryOperator/afterSimple.java @@ -2,7 +2,7 @@ class A{ void test(){ - List list = null; + List list = Math.random() > 0.5 ? new List() : null; Object o = list != null ? list.get(0) : null; } } \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceWithTernaryOperator/beforeMethodCall.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceWithTernaryOperator/beforeMethodCall.java index 9b5ec3919db9..9ac97b1c5f00 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceWithTernaryOperator/beforeMethodCall.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceWithTernaryOperator/beforeMethodCall.java @@ -4,7 +4,7 @@ import org.jetbrains.annotations.NotNull; class A{ void test(@NotNull List l) { - final List list = null; + final List list = Math.random() > 0.5 ? new List() : null; test(list); } } \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceWithTernaryOperator/beforeSimple.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceWithTernaryOperator/beforeSimple.java index 1a4de85ae51a..2566809beaeb 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceWithTernaryOperator/beforeSimple.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceWithTernaryOperator/beforeSimple.java @@ -2,7 +2,7 @@ class A{ void test(){ - List list = null; + List list = Math.random() > 0.5 ? new List() : null; Object o = list.get(0); } } \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/surroundWithIf/after1.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/surroundWithIf/after1.java index 536555efc303..3be9f587f229 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/surroundWithIf/after1.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/surroundWithIf/after1.java @@ -1,7 +1,7 @@ // "Surround with 'if (i != null)'" "true" class A { - void foo(){ - String i = null; + void foo(int x){ + String i = x > 0 ? "" : null; if (i != null) { i.hashCode(); } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/surroundWithIf/before1.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/surroundWithIf/before1.java index 81ead46f8594..e0f31c6047b3 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/surroundWithIf/before1.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/surroundWithIf/before1.java @@ -1,7 +1,7 @@ // "Surround with 'if (i != null)'" "true" class A { - void foo(){ - String i = null; + void foo(int x){ + String i = x > 0 ? "" : null; i.hashCode(); } } \ No newline at end of file