custom nullable annotation on an element should win over package-default not-null (IDEA-138894)

This commit is contained in:
peter
2015-04-08 18:13:16 +02:00
parent 8bf079ca59
commit cfe4917c8b
3 changed files with 71 additions and 10 deletions
@@ -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<String> 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<String> 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) {
@@ -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();
}
}
@@ -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);