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

This commit is contained in:
peter
2017-05-08 16:33:19 +02:00
parent 6022466f17
commit 6b43b5ba7d
5 changed files with 68 additions and 15 deletions
@@ -69,18 +69,8 @@ public class DfaPsiUtil {
@NotNull
public static Nullness getElementNullability(@Nullable PsiType resultType, @Nullable PsiModifierListOwner owner) {
if (resultType != null) {
for (PsiAnnotation annotation : resultType.getAnnotations()) {
String qualifiedName = annotation.getQualifiedName();
NullableNotNullManager nnn = NullableNotNullManager.getInstance(annotation.getProject());
if (nnn.getNullables().contains(qualifiedName)) {
return Nullness.NULLABLE;
}
if (nnn.getNotNulls().contains(qualifiedName)) {
return Nullness.NOT_NULL;
}
}
}
Nullness x = getTypeNullability(resultType);
if (x != Nullness.UNKNOWN) return x;
if (owner == null || resultType instanceof PsiPrimitiveType) {
return Nullness.UNKNOWN;
@@ -109,6 +99,23 @@ public class DfaPsiUtil {
return Nullness.UNKNOWN;
}
@NotNull
public static Nullness getTypeNullability(@Nullable PsiType type) {
if (type != null) {
for (PsiAnnotation annotation : type.getAnnotations()) {
String qualifiedName = annotation.getQualifiedName();
NullableNotNullManager nnn = NullableNotNullManager.getInstance(annotation.getProject());
if (nnn.getNullables().contains(qualifiedName)) {
return Nullness.NULLABLE;
}
if (nnn.getNotNulls().contains(qualifiedName)) {
return Nullness.NOT_NULL;
}
}
}
return Nullness.UNKNOWN;
}
/**
* Returns the nullness of functional expression parameter
*
@@ -19,10 +19,12 @@ import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInsight.NullableNotNullManager;
import com.intellij.codeInsight.daemon.GroupNames;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
import com.intellij.codeInsight.daemon.impl.analysis.JavaGenericsUtil;
import com.intellij.codeInsight.intention.AddAnnotationPsiFix;
import com.intellij.codeInsight.intention.impl.AddNotNullAnnotationFix;
import com.intellij.codeInspection.*;
import com.intellij.codeInspection.dataFlow.DfaPsiUtil;
import com.intellij.codeInspection.dataFlow.Nullness;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.WriteExternalException;
@@ -390,6 +392,19 @@ 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));
}
}
return new Annotated(isDeclaredNotNull != null,isDeclaredNullable != null);
}
@@ -0,0 +1,21 @@
import typeUse.*;
import java.util.*;
class JC {
public static Collection<@Nullable Object> getNullableStuff() {
return Collections.emptyList();
}
public static Collection<@NotNull Object> getNotNullStuff() {
return Collections.emptyList();
}
void usage() {
for (<warning descr="Loop 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()) {
System.out.println(o.getClass());
}
}
}
@@ -26,6 +26,7 @@ import com.intellij.testFramework.IdeaTestUtil;
import com.intellij.testFramework.LightProjectDescriptor;
import com.intellij.testFramework.PsiTestUtil;
import com.intellij.testFramework.fixtures.DefaultLightProjectDescriptor;
import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture;
import org.jetbrains.annotations.NotNull;
/**
@@ -128,9 +129,13 @@ public class DataFlowInspection8Test extends DataFlowInspectionTestCase {
}
private void setupCustomAnnotations() {
myFixture.addClass("package foo;\n\nimport java.lang.annotation.*;\n\n@Target({ElementType.TYPE_USE}) public @interface Nullable { }");
myFixture.addClass("package foo;\n\nimport java.lang.annotation.*;\n\n@Target({ElementType.TYPE_USE}) public @interface NotNull { }");
setCustomAnnotations(getProject(), myFixture.getTestRootDisposable(), "foo.NotNull", "foo.Nullable");
setupTypeUseAnnotations("foo", myFixture);
}
static void setupTypeUseAnnotations(String pkg, JavaCodeInsightTestFixture fixture) {
fixture.addClass("package " + pkg + ";\n\nimport java.lang.annotation.*;\n\n@Target({ElementType.TYPE_USE}) public @interface Nullable { }");
fixture.addClass("package " + pkg + ";\n\nimport java.lang.annotation.*;\n\n@Target({ElementType.TYPE_USE}) public @interface NotNull { }");
setCustomAnnotations(fixture.getProject(), fixture.getTestRootDisposable(), pkg + ".NotNull", pkg + ".Nullable");
}
static void setCustomAnnotations(Project project, Disposable parentDisposable, String notNull, String nullable) {
@@ -202,4 +202,9 @@ public class NullableStuffInspectionTest extends LightCodeInsightFixtureTestCase
doTest();
}
public void testForeachParameterNullability() {
DataFlowInspection8Test.setupTypeUseAnnotations("typeUse", myFixture);
doTest();
}
}