IDEA-171248 Nullability: missing warning in for-each cycle

support for forEach lambda parameters
This commit is contained in:
peter
2017-06-30 15:44:06 +02:00
parent 56aacfeb3e
commit 3fb458c651
3 changed files with 37 additions and 20 deletions
@@ -17,12 +17,12 @@ package com.intellij.codeInspection.dataFlow;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInsight.NullableNotNullManager;
import com.intellij.codeInsight.daemon.impl.analysis.JavaGenericsUtil;
import com.intellij.codeInspection.dataFlow.instructions.Instruction;
import com.intellij.codeInspection.dataFlow.instructions.MethodCallInstruction;
import com.intellij.codeInspection.dataFlow.instructions.ReturnInstruction;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.util.Ref;
import com.intellij.patterns.PsiJavaPatterns;
import com.intellij.psi.*;
import com.intellij.psi.search.LocalSearchScope;
import com.intellij.psi.search.searches.ReferencesSearch;
@@ -93,15 +93,28 @@ public class DfaPsiUtil {
Nullness fromType = getTypeNullability(resultType);
if (fromType != Nullness.UNKNOWN) return fromType;
if (PsiJavaPatterns.psiParameter().withParents(PsiParameterList.class, PsiLambdaExpression.class).accepts(owner)) {
PsiLambdaExpression lambda = (PsiLambdaExpression)owner.getParent().getParent();
int index = lambda.getParameterList().getParameterIndex((PsiParameter)owner);
return getFunctionalParameterNullability(lambda, index);
if (owner instanceof PsiParameter) {
return inferParameterNullability((PsiParameter)owner);
}
return Nullness.UNKNOWN;
}
@NotNull
public static Nullness inferParameterNullability(@NotNull PsiParameter parameter) {
PsiElement parent = parameter.getParent();
if (parent instanceof PsiParameterList && parent.getParent() instanceof PsiLambdaExpression) {
return getFunctionalParameterNullability((PsiLambdaExpression)parent.getParent(), ((PsiParameterList)parent).getParameterIndex(parameter));
}
if (parent instanceof PsiForeachStatement) {
PsiExpression iteratedValue = ((PsiForeachStatement)parent).getIteratedValue();
if (iteratedValue != null) {
return getTypeNullability(JavaGenericsUtil.getCollectionItemType(iteratedValue));
}
}
return Nullness.UNKNOWN;
}
@NotNull
public static Nullness getTypeNullability(@Nullable PsiType type) {
if (type == null) return Nullness.UNKNOWN;
@@ -142,7 +155,7 @@ public class DfaPsiUtil {
if(nullness != Nullness.UNKNOWN) {
return nullness;
}
PsiClassType type = ObjectUtils.tryCast(function.getFunctionalInterfaceType(), PsiClassType.class);
PsiClassType type = ObjectUtils.tryCast(LambdaUtil.getFunctionalInterfaceType(function, true), PsiClassType.class);
PsiMethod sam = LambdaUtil.getFunctionalInterfaceMethod(type);
if (sam != null && index < sam.getParameterList().getParametersCount()) {
PsiParameter parameter = sam.getParameterList().getParameters()[index];
@@ -151,7 +164,7 @@ public class DfaPsiUtil {
return nullness;
}
PsiType parameterType = type.resolveGenerics().getSubstitutor().substitute(parameter.getType());
return getTypeNullability(parameterType);
return getTypeNullability(GenericsUtil.eliminateWildcards(parameterType, false, true));
}
return Nullness.UNKNOWN;
}
@@ -523,22 +523,24 @@ public class NullableStuffInspectionBase extends BaseJavaBatchLocalInspectionToo
PsiAnnotation annotation = isDeclaredNotNull == null ? isDeclaredNullable : isDeclaredNotNull;
reportPrimitiveType(holder, annotation, annotation, parameter);
}
if (parameter.getParent() instanceof PsiForeachStatement) {
PsiExpression iteratedValue = ((PsiForeachStatement)parameter.getParent()).getIteratedValue();
Nullness itemTypeNullability = DfaPsiUtil.getTypeNullability(iteratedValue == null ? null : JavaGenericsUtil.getCollectionItemType(iteratedValue));
if (isDeclaredNotNull != null && itemTypeNullability == Nullness.NULLABLE) {
holder.registerProblem(isDeclaredNotNull, "Loop parameter can be null",
new RemoveAnnotationQuickFix(isDeclaredNotNull, null));
}
else if (isDeclaredNullable != null && itemTypeNullability == Nullness.NOT_NULL) {
holder.registerProblem(isDeclaredNullable, "Loop parameter is always not-null",
new RemoveAnnotationQuickFix(isDeclaredNullable, null));
}
if (parameter instanceof PsiParameter) {
checkLoopParameterNullability(holder, isDeclaredNotNull, isDeclaredNullable, DfaPsiUtil.inferParameterNullability((PsiParameter)parameter));
}
return new Annotated(isDeclaredNotNull != null,isDeclaredNullable != null);
}
private static void checkLoopParameterNullability(ProblemsHolder holder, @Nullable PsiAnnotation notNull, @Nullable PsiAnnotation nullable, Nullness expectedNullability) {
if (notNull != null && expectedNullability == Nullness.NULLABLE) {
holder.registerProblem(notNull, "Parameter can be null",
new RemoveAnnotationQuickFix(notNull, null));
}
else if (nullable != null && expectedNullability == Nullness.NOT_NULL) {
holder.registerProblem(nullable, "Parameter is always not-null",
new RemoveAnnotationQuickFix(nullable, null));
}
}
private static void reportPrimitiveType(final ProblemsHolder holder, final PsiElement psiElement, final PsiAnnotation annotation,
final PsiModifierListOwner listOwner) {
holder.registerProblem(psiElement.isPhysical() ? psiElement : listOwner.getNavigationElement(),
@@ -11,11 +11,13 @@ class JC {
}
void usage() {
for (<warning descr="Loop parameter can be null">@NotNull</warning> Object o : getNullableStuff()) {
for (<warning descr="Parameter can be null">@NotNull</warning> Object o : getNullableStuff()) {
System.out.println(o.getClass());
}
for (<warning descr="Loop parameter is always not-null">@Nullable</warning> Object o : getNotNullStuff()) {
for (<warning descr="Parameter is always not-null">@Nullable</warning> Object o : getNotNullStuff()) {
System.out.println(o.getClass());
}
getNullableStuff().forEach((<warning descr="Parameter can be null">@NotNull</warning> Object s) -> System.out.println(s.hashCode()));
getNotNullStuff().forEach((<warning descr="Parameter is always not-null">@Nullable</warning> Object s) -> System.out.println(s.hashCode()));
}
}