diff --git a/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java b/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java index 680435292663..6e74999a2875 100644 --- a/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java +++ b/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java @@ -116,7 +116,7 @@ public abstract class NullableNotNullManager implements PersistentStateComponent @Nullable public PsiAnnotation getNullableAnnotation(@NotNull PsiModifierListOwner owner, boolean checkBases) { - return findNullabilityAnnotation(owner, checkBases, true); + return findNullabilityAnnotationWithDefault(owner, checkBases, true); } public boolean isContainerAnnotation(@NotNull PsiAnnotation anno) { @@ -136,7 +136,7 @@ public abstract class NullableNotNullManager implements PersistentStateComponent @Nullable public PsiAnnotation getNotNullAnnotation(@NotNull PsiModifierListOwner owner, boolean checkBases) { - return findNullabilityAnnotation(owner, checkBases, false); + return findNullabilityAnnotationWithDefault(owner, checkBases, false); } public PsiAnnotation copyNotNullAnnotation(PsiModifierListOwner owner) { @@ -168,14 +168,14 @@ public abstract class NullableNotNullManager implements PersistentStateComponent } @Nullable - private PsiAnnotation findNullabilityAnnotation(@NotNull PsiModifierListOwner owner, boolean checkBases, boolean nullable) { - Set qNames = ContainerUtil.newHashSet(nullable ? getNullables() : getNotNulls()); - PsiAnnotation annotation = checkBases && owner instanceof PsiMethod - ? AnnotationUtil.findAnnotationInHierarchy(owner, qNames) - : AnnotationUtil.findAnnotation(owner, qNames); + private PsiAnnotation findNullabilityAnnotationWithDefault(@NotNull PsiModifierListOwner owner, boolean checkBases, boolean nullable) { + PsiAnnotation annotation = findPlainNullabilityAnnotation(owner, checkBases, nullable); if (annotation != null) { return annotation; } + if (findPlainNullabilityAnnotation(owner, checkBases, !nullable) != null) { + return null; + } PsiType type = getOwnerType(owner); if (type == null || TypeConversionUtil.isPrimitiveAndNotNull(type)) return null; @@ -192,6 +192,13 @@ public abstract class NullableNotNullManager implements PersistentStateComponent return findNullabilityDefaultInHierarchy(owner, nullable); } + private PsiAnnotation findPlainNullabilityAnnotation(@NotNull PsiModifierListOwner owner, boolean checkBases, boolean nullable) { + Set qNames = ContainerUtil.newHashSet(nullable ? getNullables() : getNotNulls()); + return checkBases && owner instanceof PsiMethod + ? AnnotationUtil.findAnnotationInHierarchy(owner, qNames) + : AnnotationUtil.findAnnotation(owner, qNames); + } + protected boolean hasHardcodedContracts(PsiElement element) { return false; } @@ -204,11 +211,11 @@ public abstract class NullableNotNullManager implements PersistentStateComponent } public boolean isNullable(@NotNull PsiModifierListOwner owner, boolean checkBases) { - return findNullabilityAnnotation(owner, checkBases, true) != null; + return findNullabilityAnnotationWithDefault(owner, checkBases, true) != null; } public boolean isNotNull(@NotNull PsiModifierListOwner owner, boolean checkBases) { - return findNullabilityAnnotation(owner, checkBases, false) != null; + return findNullabilityAnnotationWithDefault(owner, checkBases, false) != null; } @Nullable @@ -331,7 +338,7 @@ public abstract class NullableNotNullManager implements PersistentStateComponent } public static boolean isNullable(@NotNull PsiModifierListOwner owner) { - return !isNotNull(owner) && getInstance(owner.getProject()).isNullable(owner, true); + return getInstance(owner.getProject()).isNullable(owner, true); } public static boolean isNotNull(@NotNull PsiModifierListOwner owner) { diff --git a/java/java-tests/testData/inspection/nullableProblems/OverrideCustomDefault.java b/java/java-tests/testData/inspection/nullableProblems/OverrideCustomDefault.java new file mode 100644 index 000000000000..a274c4a37153 --- /dev/null +++ b/java/java-tests/testData/inspection/nullableProblems/OverrideCustomDefault.java @@ -0,0 +1,24 @@ +package foo; + +import custom.CheckForNull; + +class BaseClass { + public void foo() { + Object nullable = getNullable(); + if (nullable != null) { + System.out.println(nullable.toString()); + } + } + + @CheckForNull Object getNullable() { + return null; + } +} + +class ChildClass extends BaseClass { + @CheckForNull + @Override + Object getNullable() { + return super.getNullable(); + } +} diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/NullableStuffInspectionTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/NullableStuffInspectionTest.java index eadd7cf127f6..e0c38a3913f4 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/NullableStuffInspectionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/NullableStuffInspectionTest.java @@ -7,8 +7,11 @@ package com.intellij.codeInspection; import com.intellij.JavaTestUtil; +import com.intellij.codeInsight.NullableNotNullManager; import com.intellij.codeInspection.nullable.NullableStuffInspection; +import com.intellij.openapi.Disposable; import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.util.Disposer; import com.intellij.testFramework.LightProjectDescriptor; import com.intellij.testFramework.PsiTestUtil; import com.intellij.testFramework.fixtures.DefaultLightProjectDescriptor; @@ -112,6 +115,33 @@ public class NullableStuffInspectionTest extends LightCodeInsightFixtureTestCase myFixture.checkHighlighting(true, false, true); } + public void testOverrideCustomDefault() { + DataFlowInspectionTest.addJavaxNullabilityAnnotations(myFixture); + myFixture.addClass("package custom;" + + "public @interface CheckForNull {}"); + + final NullableNotNullManager nnnManager = NullableNotNullManager.getInstance(getProject()); + nnnManager.setNullables("custom.CheckForNull"); + Disposer.register(myTestRootDisposable, new Disposable() { + @Override + public void dispose() { + nnnManager.setNullables(); + } + }); + + myFixture.addClass("package foo;" + + "import static java.lang.annotation.ElementType.*;" + + "@javax.annotation.meta.TypeQualifierDefault(METHOD) " + + "@javax.annotation.Nonnull " + + "public @interface ReturnValuesAreNonnullByDefault {}"); + + myFixture.addFileToProject("foo/package-info.java", "@ReturnValuesAreNonnullByDefault package foo;"); + + myFixture.configureFromExistingVirtualFile(myFixture.copyFileToProject(getTestName(false) + ".java", "foo/Classes.java")); + myFixture.enableInspections(myInspection); + myFixture.checkHighlighting(true, false, true); + } + public void testHonorParameterDefaultInSetters() { DataFlowInspectionTest.addJavaxNullabilityAnnotations(myFixture); DataFlowInspectionTest.addJavaxDefaultNullabilityAnnotations(myFixture);