[java-dfa] Do not dereference method reference qualifier for static method references

Fixes IDEA-290952 Constant conditions & expression warns about NullPointerException for Objects.nonNull with checked "Treat non-annotated members and parameters as @Nullable"

GitOrigin-RevId: 88b063f12031b85dfa9fb7a009f75ebf0673760a
This commit is contained in:
Tagir Valeev
2022-03-27 17:11:58 +00:00
committed by intellij-monorepo-bot
parent 9f2ea8fe74
commit 299d0bed02
4 changed files with 34 additions and 5 deletions
@@ -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()) {
@@ -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<? extends MethodContract> 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());
@@ -0,0 +1,13 @@
import java.util.*;
import java.util.function.*;
import typeUse.*;
public class ObjectsNonNullWithUnknownNullable {
void foo(@NotNull List<@NotNull String> list) {
Predicate<String> predicate = Objects::nonNull;
list.stream()
.map(s -> s.isEmpty() ? null : s)
.filter(Objects::nonNull)
.forEach(System.out::println);
}
}
@@ -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(); }