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 8bd12bc6d1c2..84df05bb76c9 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
@@ -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
*
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/nullable/NullableStuffInspectionBase.java b/java/java-analysis-impl/src/com/intellij/codeInspection/nullable/NullableStuffInspectionBase.java
index 08e7257d722f..f1d0858eb5a5 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/nullable/NullableStuffInspectionBase.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/nullable/NullableStuffInspectionBase.java
@@ -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);
}
diff --git a/java/java-tests/testData/inspection/nullableProblems/ForeachParameterNullability.java b/java/java-tests/testData/inspection/nullableProblems/ForeachParameterNullability.java
new file mode 100644
index 000000000000..5d4268a5c4d3
--- /dev/null
+++ b/java/java-tests/testData/inspection/nullableProblems/ForeachParameterNullability.java
@@ -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 (@NotNull Object o : getNullableStuff()) {
+ System.out.println(o.getClass());
+ }
+ for (@Nullable Object o : getNotNullStuff()) {
+ System.out.println(o.getClass());
+ }
+ }
+}
\ No newline at end of file
diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspection8Test.java b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspection8Test.java
index 169861bee57c..cd4dec7f967c 100644
--- a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspection8Test.java
+++ b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspection8Test.java
@@ -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) {
diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/NullableStuffInspectionTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/NullableStuffInspectionTest.java
index 96e934844885..4f95f5cd5494 100644
--- a/java/java-tests/testSrc/com/intellij/codeInspection/NullableStuffInspectionTest.java
+++ b/java/java-tests/testSrc/com/intellij/codeInspection/NullableStuffInspectionTest.java
@@ -202,4 +202,9 @@ public class NullableStuffInspectionTest extends LightCodeInsightFixtureTestCase
doTest();
}
+ public void testForeachParameterNullability() {
+ DataFlowInspection8Test.setupTypeUseAnnotations("typeUse", myFixture);
+ doTest();
+ }
+
}
\ No newline at end of file