diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaPsiUtil.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaPsiUtil.java index 651713ddb2dd..b3907058b61e 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaPsiUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaPsiUtil.java @@ -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; + } } 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 068744b18492..54498e8109cd 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 @@ -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 a type of anchor element which could be associated with given nullability problem kind */ public class NullabilityProblemKind { - 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 { 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 { } } } + 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; } diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/TypeUseVarArg.java b/java/java-tests/testData/inspection/dataFlow/fixture/TypeUseVarArg.java new file mode 100644 index 000000000000..030668528e2e --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/TypeUseVarArg.java @@ -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(nullArr); + nullArr = null; + foo2(nullArr); + nullArr = null; + foo3(nullArr); + foo1(null, ""); + foo2(null, ""); + foo3(null, ""); + } +} \ 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 c3ced503f896..faa1dd3c943b 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection8Test.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection8Test.java @@ -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(); + } } \ No newline at end of file