diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/inst/MethodCallInstruction.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/inst/MethodCallInstruction.java index 6362e3c9e12d..0b0bd218cd74 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/inst/MethodCallInstruction.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/inst/MethodCallInstruction.java @@ -441,7 +441,9 @@ public class MethodCallInstruction extends ExpressionPushingInstruction { DfaValue value = memState.pop(); if (getContext() instanceof PsiMethodReferenceExpression) { PsiMethodReferenceExpression context = (PsiMethodReferenceExpression)getContext(); - value = CheckNotNullInstruction.dereference(interpreter, memState, value, NullabilityProblemKind.callMethodRefNPE.problem(context, null)); + if (MethodReferenceInstruction.isQualifierDereferenced(context)) { + value = CheckNotNullInstruction.dereference(interpreter, memState, value, NullabilityProblemKind.callMethodRefNPE.problem(context, null)); + } } DfType dfType = memState.getDfType(value); if (getMutationSignature().mutatesThis() && !Mutability.fromDfType(dfType).canBeModified()) { diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/inst/MethodReferenceInstruction.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/inst/MethodReferenceInstruction.java index af88398b4914..36ebedddf16f 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/inst/MethodReferenceInstruction.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/inst/MethodReferenceInstruction.java @@ -56,7 +56,10 @@ public class MethodReferenceInstruction extends ExpressionPushingInstruction { if (method == null) return; PsiSubstitutor substitutor = resolveResult.getSubstitutor(); DfaCallArguments callArguments = getMethodReferenceCallArguments(state, methodRef, qualifier, interpreter, sam, method, substitutor); - CheckNotNullInstruction.dereference(interpreter, state, callArguments.getQualifier(), NullabilityProblemKind.callMethodRefNPE.problem(methodRef, null)); + if (isQualifierDereferenced(methodRef)) { + CheckNotNullInstruction.dereference(interpreter, state, callArguments.getQualifier(), + NullabilityProblemKind.callMethodRefNPE.problem(methodRef, null)); + } List contracts = JavaMethodContractUtil.getMethodCallContracts(method, null); if (contracts.isEmpty() || !JavaMethodContractUtil.isPure(method)) return; PsiType returnType = substitutor.substitute(method.getReturnType()); @@ -81,6 +84,15 @@ public class MethodReferenceInstruction extends ExpressionPushingInstruction { } } + static boolean isQualifierDereferenced(@NotNull PsiMethodReferenceExpression methodRef) { + if (methodRef.isConstructor()) return false; + PsiElement target = methodRef.resolve(); + if (!(target instanceof PsiMethod)) return false; + if (((PsiMethod)target).hasModifierProperty(PsiModifier.STATIC)) return false; + if (!PsiMethodReferenceUtil.isStaticallyReferenced(methodRef)) return false; + return true; + } + private static @NotNull DfaCallArguments getMethodReferenceCallArguments(@NotNull DfaMemoryState state, @NotNull PsiMethodReferenceExpression methodRef, DfaValue qualifier, @@ -89,9 +101,7 @@ public class MethodReferenceInstruction extends ExpressionPushingInstruction { @NotNull PsiMethod method, @NotNull PsiSubstitutor substitutor) { PsiParameter[] samParameters = sam.getParameterList().getParameters(); - boolean isStatic = method.hasModifierProperty(PsiModifier.STATIC); - boolean instanceBound = !isStatic && !method.isConstructor() && !PsiMethodReferenceUtil.isStaticallyReferenced(methodRef); - boolean firstParameterIsQualifier = !isStatic && !instanceBound && !method.isConstructor(); + boolean firstParameterIsQualifier = isQualifierDereferenced(methodRef); PsiParameter[] parameters = method.getParameterList().getParameters(); DfaValue[] arguments = new DfaValue[parameters.length]; Arrays.fill(arguments, interpreter.getFactory().getUnknown()); diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/ObjectsNonNullWithUnknownNullable.java b/java/java-tests/testData/inspection/dataFlow/fixture/ObjectsNonNullWithUnknownNullable.java new file mode 100644 index 000000000000..1a8336559a89 --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/ObjectsNonNullWithUnknownNullable.java @@ -0,0 +1,13 @@ +import java.util.*; +import java.util.function.*; +import typeUse.*; + +public class ObjectsNonNullWithUnknownNullable { + void foo(@NotNull List<@NotNull String> list) { + Predicate predicate = Objects::nonNull; + list.stream() + .map(s -> s.isEmpty() ? null : s) + .filter(Objects::nonNull) + .forEach(System.out::println); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection8Test.java b/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection8Test.java index d776892a56db..38c069c9b4f9 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection8Test.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection8Test.java @@ -34,6 +34,10 @@ public class DataFlowInspection8Test extends DataFlowInspectionTestCase { public void testUnboxingBoxingInLambdaReturn() { doTest(); } public void testUnboxingInMethodReferences() { doTest(); } public void testMethodReferenceOnNullable() { doTest(); } + public void testObjectsNonNullWithUnknownNullable() { + setupTypeUseAnnotations("typeUse", myFixture); + doTestWith(insp -> insp.TREAT_UNKNOWN_MEMBERS_AS_NULLABLE = true); + } public void testNullableVoidLambda() { doTest(); } public void testNullableForeachVariable() { doTestWithCustomAnnotations(); } public void testGenericParameterNullity() { doTestWithCustomAnnotations(); }