mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-224530 Support nullability check for vararg components
GitOrigin-RevId: 884b14dcf050d528e5a71ddebda9ce77eb73d189
This commit is contained in:
committed by
intellij-monorepo-bot
parent
d1ca1a5530
commit
4f50292978
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+12
-4
@@ -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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user