IDEA-224530 Support nullability check for vararg components

GitOrigin-RevId: 884b14dcf050d528e5a71ddebda9ce77eb73d189
This commit is contained in:
Tagir Valeev
2019-10-10 04:41:20 +00:00
committed by intellij-monorepo-bot
parent d1ca1a5530
commit 4f50292978
4 changed files with 69 additions and 7 deletions
@@ -26,13 +26,22 @@ import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.JBIterable;
import com.intellij.util.containers.MultiMap;
import com.intellij.util.containers.Stack;
import com.siyeh.ig.callMatcher.CallMatcher;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import static com.intellij.psi.CommonClassNames.*;
import static com.siyeh.ig.callMatcher.CallMatcher.staticCall;
public class DfaPsiUtil {
private static final CallMatcher NON_NULL_VAR_ARG = CallMatcher.anyOf(
staticCall(JAVA_UTIL_LIST, "of"),
staticCall(JAVA_UTIL_SET, "of"),
staticCall(JAVA_UTIL_MAP, "ofEntries"));
public static boolean isFinalField(PsiVariable var) {
return var.hasModifierProperty(PsiModifier.FINAL) && !var.hasModifierProperty(PsiModifier.TRANSIENT) && var instanceof PsiField;
}
@@ -248,7 +257,7 @@ public class DfaPsiUtil {
private static Nullability getLambdaParameterNullability(@NotNull PsiMethod method, int parameterIndex, int lambdaParameterIndex) {
PsiClass type = method.getContainingClass();
if(type != null) {
if(CommonClassNames.JAVA_UTIL_OPTIONAL.equals(type.getQualifiedName())) {
if(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) {
@@ -266,7 +275,7 @@ public class DfaPsiUtil {
if (containingClass != null && containingClass.isEnum()) {
PsiParameter[] parameters = method.getParameterList().getParameters();
if ("values".equals(methodName)) return parameters.length == 0;
return parameters.length == 1 && parameters[0].getType().equalsToText(CommonClassNames.JAVA_LANG_STRING);
return parameters.length == 1 && parameters[0].getType().equalsToText(JAVA_LANG_STRING);
}
}
return false;
@@ -364,7 +373,7 @@ public class DfaPsiUtil {
}
}
}
return Result.create(notNullFields, constructor, PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT);
return Result.create(notNullFields, constructor, PsiModificationTracker.MODIFICATION_COUNT);
}
});
}
@@ -502,4 +511,26 @@ public class DfaPsiUtil {
}
return false;
}
/**
* @param method method to check
* @return nullability of vararg parameter component; {@link Nullability#UNKNOWN} if not specified or method is not vararg method.
*/
@NotNull
static Nullability getVarArgComponentNullability(PsiMethod method) {
if (method != null) {
if (NON_NULL_VAR_ARG.methodMatches(method)) {
return Nullability.NOT_NULL;
}
PsiParameter varArg = ArrayUtil.getLastElement(method.getParameterList().getParameters());
if (varArg != null) {
PsiType type = varArg.getType();
if (type instanceof PsiEllipsisType) {
PsiType componentType = ((PsiEllipsisType)type).getComponentType();
return getTypeNullability(componentType);
}
}
}
return Nullability.UNKNOWN;
}
}
@@ -23,6 +23,8 @@ import java.util.function.Consumer;
import java.util.function.Function;
import static com.intellij.codeInspection.InspectionsBundle.BUNDLE;
import static com.intellij.psi.CommonClassNames.JAVA_LANG_NULL_POINTER_EXCEPTION;
import static com.intellij.psi.CommonClassNames.JAVA_LANG_RUNTIME_EXCEPTION;
import static com.intellij.util.ObjectUtils.tryCast;
/**
@@ -30,9 +32,9 @@ import static com.intellij.util.ObjectUtils.tryCast;
* @param <T> a type of anchor element which could be associated with given nullability problem kind
*/
public class NullabilityProblemKind<T extends PsiElement> {
private static final String NPE = CommonClassNames.JAVA_LANG_NULL_POINTER_EXCEPTION;
private static final String RE = CommonClassNames.JAVA_LANG_RUNTIME_EXCEPTION;
private static final String NPE = JAVA_LANG_NULL_POINTER_EXCEPTION;
private static final String RE = JAVA_LANG_RUNTIME_EXCEPTION;
private final String myName;
private final String myAlwaysNullMessage;
private final String myNormalMessage;
@@ -247,11 +249,11 @@ public class NullabilityProblemKind<T extends PsiElement> {
return fieldAccessNPE.problem(context, expression);
}
PsiParameter parameter = MethodCallUtils.getParameterForArgument(context);
PsiElement grandParent = expressionList.getParent();
if (parameter != null) {
if (parameter.getType() instanceof PsiPrimitiveType) {
return createUnboxingProblem(context, expression);
}
PsiElement grandParent = expressionList.getParent();
if (grandParent instanceof PsiAnonymousClass) {
grandParent = grandParent.getParent();
}
@@ -266,6 +268,12 @@ public class NullabilityProblemKind<T extends PsiElement> {
}
}
}
else if (grandParent instanceof PsiCall && MethodCallUtils.isVarArgCall((PsiCall)grandParent)) {
Nullability nullability = DfaPsiUtil.getVarArgComponentNullability(((PsiCall)grandParent).resolveMethod());
if (nullability == Nullability.NOT_NULL) {
return passingToNotNullParameter.problem(context, expression);
}
}
return null;
}
@@ -0,0 +1,19 @@
import typeUse.*;
class App {
void foo1(@NotNull String @NotNull ... args) {}
void foo2(String @NotNull ... args) {}
void foo3(@NotNull String ... args) {}
void use() {
String[] nullArr = null;
foo1(<warning descr="Passing 'null' argument to parameter annotated as @NotNull">nullArr</warning>);
nullArr = null;
foo2(<warning descr="Passing 'null' argument to parameter annotated as @NotNull">nullArr</warning>);
nullArr = null;
foo3(<warning descr="Passing 'null' argument to non-annotated parameter">nullArr</warning>);
foo1(<warning descr="Passing 'null' argument to parameter annotated as @NotNull">null</warning>, "");
foo2(null, "");
foo3(<warning descr="Passing 'null' argument to parameter annotated as @NotNull">null</warning>, "");
}
}
@@ -251,4 +251,8 @@ public class DataFlowInspection8Test extends DataFlowInspectionTestCase {
public void testLambdaWritesArrayInTry() { doTest(); }
public void testManyNestedOptionals() { doTest(); }
public void testGetClass() { doTest(); }
public void testTypeUseVarArg() {
setupTypeUseAnnotations("typeUse", myFixture);
doTest();
}
}