IDEA-160428 Lambdas passed to Optional.map/filter/flatMap/ifPresent could have their argument implicitly marked as @NotNull

This commit is contained in:
Tagir Valeev
2016-08-29 19:03:38 +03:00
parent e698c47bb7
commit 625c740f99
3 changed files with 54 additions and 1 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,6 +29,7 @@ import com.intellij.psi.search.LocalSearchScope;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.*;
import com.intellij.util.ArrayUtil;
import com.intellij.util.NullableFunction;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.MultiMap;
@@ -102,6 +103,10 @@ public class DfaPsiUtil {
if (PsiJavaPatterns.psiParameter().withParents(PsiParameterList.class, PsiLambdaExpression.class).accepts(owner)) {
PsiLambdaExpression lambda = (PsiLambdaExpression)owner.getParent().getParent();
int index = lambda.getParameterList().getParameterIndex((PsiParameter)owner);
Nullness nullness = inferLambdaParameterNullness(lambda, index);
if(nullness != Nullness.UNKNOWN) {
return nullness;
}
PsiMethod sam = LambdaUtil.getFunctionalInterfaceMethod(lambda.getFunctionalInterfaceType());
if (sam != null && index < sam.getParameterList().getParametersCount()) {
return getElementNullability(null, sam.getParameterList().getParameters()[index]);
@@ -111,6 +116,43 @@ public class DfaPsiUtil {
return Nullness.UNKNOWN;
}
@NotNull
private static Nullness inferLambdaParameterNullness(PsiLambdaExpression lambda, int parameterIndex) {
PsiElement expression = lambda;
PsiElement expressionParent = lambda.getParent();
while(expressionParent instanceof PsiConditionalExpression) {
expression = expressionParent;
expressionParent = expressionParent.getParent();
}
if(expressionParent instanceof PsiExpressionList) {
PsiExpressionList list = (PsiExpressionList)expressionParent;
PsiElement listParent = list.getParent();
if(listParent instanceof PsiMethodCallExpression) {
PsiMethod method = ((PsiMethodCallExpression)listParent).resolveMethod();
if(method != null) {
int expressionIndex = ArrayUtil.find(list.getExpressions(), expression);
return getLambdaParameterNullness(method, expressionIndex, parameterIndex);
}
}
}
return Nullness.UNKNOWN;
}
@NotNull
private static Nullness getLambdaParameterNullness(@NotNull PsiMethod method, int parameterIndex, int lambdaParameterIndex) {
PsiClass type = method.getContainingClass();
if(type != null) {
if(CommonClassNames.JAVA_UTIL_OPTIONAL.equals(type.getQualifiedName())) {
String methodName = method.getName();
if((methodName.equals("map") || methodName.equals("filter") || methodName.equals("ifPresent") || methodName.equals("flatMap"))
&& parameterIndex == 0 && lambdaParameterIndex == 0) {
return Nullness.NOT_NULL;
}
}
}
return Nullness.UNKNOWN;
}
private static boolean isNotNullLocally(@NotNull PsiModifierListOwner owner) {
NullableNotNullManager nnnm = NullableNotNullManager.getInstance(owner.getProject());
PsiAnnotation notNullAnno = nnnm.getNotNullAnnotation(owner, true);
@@ -0,0 +1,10 @@
import java.util.stream.Stream;
class Example {
public static void main(String[] args) {
Stream.of(args).filter(arg -> arg.startsWith("a"))
.findFirst().ifPresent(args.length == 1 ? value -> {} : value -> {
System.out.println(<warning descr="Condition 'value == null' is always 'false'">value == null</warning> ? "none" : value);
});
}
}
@@ -49,6 +49,7 @@ public class DataFlowInspection8Test extends DataFlowInspectionTestCase {
public void testOptionalOfNullable() { doTest(); }
public void testPrimitiveInVoidLambda() { doTest(); }
public void testNotNullLambdaParameter() { doTest(); }
public void testNotNullOptionalLambdaParameter() { doTest(); }
public void testNullArgumentIsFailingMethodCall() {
doTest();