summaryrefslogtreecommitdiff
path: root/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java')
-rw-r--r--java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java72
1 files changed, 59 insertions, 13 deletions
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 21e014e4aca3..2210e02a080a 100644
--- a/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java
+++ b/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java
@@ -44,10 +44,13 @@ public class NullableNotNullManager implements PersistentStateComponent<Element>
public final JDOMExternalizableStringList myNullables = new JDOMExternalizableStringList();
public final JDOMExternalizableStringList myNotNulls = new JDOMExternalizableStringList();
- public static final String[] DEFAULT_NULLABLES = {AnnotationUtil.NULLABLE, "javax.annotation.Nullable",
+ private static final String JAVAX_ANNOTATION_NULLABLE = "javax.annotation.Nullable";
+ private static final String JAVAX_ANNOTATION_NONNULL = "javax.annotation.Nonnull";
+
+ public static final String[] DEFAULT_NULLABLES = {AnnotationUtil.NULLABLE, JAVAX_ANNOTATION_NULLABLE,
"edu.umd.cs.findbugs.annotations.Nullable", "android.support.annotation.Nullable"
};
- public static final String[] DEFAULT_NOT_NULLS = {AnnotationUtil.NOT_NULL, "javax.annotation.Nonnull",
+ public static final String[] DEFAULT_NOT_NULLS = {AnnotationUtil.NOT_NULL, JAVAX_ANNOTATION_NONNULL,
"edu.umd.cs.findbugs.annotations.NonNull", "android.support.annotation.NonNull"
};
@@ -103,6 +106,11 @@ public class NullableNotNullManager implements PersistentStateComponent<Element>
return findNullabilityAnnotation(owner, checkBases, true);
}
+ public boolean isContainerAnnotation(@NotNull PsiAnnotation anno) {
+ PsiAnnotation.TargetType[] acceptAnyTarget = PsiAnnotation.TargetType.values();
+ return isNullabilityDefault(anno, true, acceptAnyTarget) || isNullabilityDefault(anno, false, acceptAnyTarget);
+ }
+
public void setDefaultNullable(@NotNull String defaultNullable) {
LOG.assertTrue(getNullables().contains(defaultNullable));
myDefaultNullable = defaultNullable;
@@ -138,15 +146,20 @@ public class NullableNotNullManager implements PersistentStateComponent<Element>
return annotation;
}
- if (owner instanceof PsiParameter && !TypeConversionUtil.isPrimitiveAndNotNull(((PsiParameter)owner).getType())) {
- // even if javax.annotation.Nullable is not configured, it should still take precedence over ByDefault annotations
- if (AnnotationUtil.isAnnotated(owner, nullable ? Arrays.asList(DEFAULT_NOT_NULLS) : Arrays.asList(DEFAULT_NULLABLES), checkBases, false)) {
- return null;
- }
- return findContainerAnnotation(owner, nullable
- ? "javax.annotation.ParametersAreNullableByDefault"
- : "javax.annotation.ParametersAreNonnullByDefault");
+ PsiType type = getOwnerType(owner);
+ if (type == null || TypeConversionUtil.isPrimitiveAndNotNull(type)) return null;
+
+ // even if javax.annotation.Nullable is not configured, it should still take precedence over ByDefault annotations
+ if (AnnotationUtil.isAnnotated(owner, nullable ? Arrays.asList(DEFAULT_NOT_NULLS) : Arrays.asList(DEFAULT_NULLABLES), checkBases, false)) {
+ return null;
}
+ return findNullabilityDefaultInHierarchy(owner, nullable);
+ }
+
+ @Nullable
+ private static PsiType getOwnerType(PsiModifierListOwner owner) {
+ if (owner instanceof PsiVariable) return ((PsiVariable)owner).getType();
+ if (owner instanceof PsiMethod) return ((PsiMethod)owner).getReturnType();
return null;
}
@@ -159,11 +172,13 @@ public class NullableNotNullManager implements PersistentStateComponent<Element>
}
@Nullable
- private static PsiAnnotation findContainerAnnotation(PsiModifierListOwner owner, String annotationFQN) {
+ private static PsiAnnotation findNullabilityDefaultInHierarchy(PsiModifierListOwner owner, boolean nullable) {
+ PsiAnnotation.TargetType[] placeTargetTypes = AnnotationTargetUtil.getTargetsForLocation(owner.getModifierList());
+
PsiElement element = owner.getParent();
while (element != null) {
if (element instanceof PsiModifierListOwner) {
- PsiAnnotation annotation = AnnotationUtil.findAnnotation((PsiModifierListOwner)element, annotationFQN);
+ PsiAnnotation annotation = getNullabilityDefault((PsiModifierListOwner)element, nullable, placeTargetTypes);
if (annotation != null) {
return annotation;
}
@@ -172,7 +187,7 @@ public class NullableNotNullManager implements PersistentStateComponent<Element>
if (element instanceof PsiClassOwner) {
String packageName = ((PsiClassOwner)element).getPackageName();
PsiPackage psiPackage = JavaPsiFacade.getInstance(element.getProject()).findPackage(packageName);
- return AnnotationUtil.findAnnotation(psiPackage, annotationFQN);
+ return psiPackage == null ? null : getNullabilityDefault(psiPackage, nullable, placeTargetTypes);
}
element = element.getContext();
@@ -180,6 +195,37 @@ public class NullableNotNullManager implements PersistentStateComponent<Element>
return null;
}
+ private static PsiAnnotation getNullabilityDefault(@NotNull PsiModifierListOwner container, boolean nullable, PsiAnnotation.TargetType[] placeTargetTypes) {
+ PsiModifierList modifierList = container.getModifierList();
+ if (modifierList == null) return null;
+ for (PsiAnnotation annotation : modifierList.getAnnotations()) {
+ if (isNullabilityDefault(annotation, nullable, placeTargetTypes)) {
+ return annotation;
+ }
+ }
+ return null;
+ }
+
+ private static boolean isNullabilityDefault(@NotNull PsiAnnotation annotation, boolean nullable, PsiAnnotation.TargetType[] placeTargetTypes) {
+ PsiJavaCodeReferenceElement element = annotation.getNameReferenceElement();
+ PsiElement declaration = element == null ? null : element.resolve();
+ if (!(declaration instanceof PsiClass)) return false;
+
+ if (!AnnotationUtil.isAnnotated((PsiClass)declaration,
+ nullable ? JAVAX_ANNOTATION_NULLABLE : JAVAX_ANNOTATION_NONNULL,
+ false,
+ true)) {
+ return false;
+ }
+
+ PsiAnnotation tqDefault = AnnotationUtil.findAnnotation((PsiClass)declaration, true, "javax.annotation.meta.TypeQualifierDefault");
+ if (tqDefault == null) return false;
+
+ Set<PsiAnnotation.TargetType> required = AnnotationTargetUtil.extractRequiredAnnotationTargets(tqDefault.findAttributeValue(null));
+ if (required == null) return false;
+ return required.isEmpty() || ContainerUtil.intersects(required, Arrays.asList(placeTargetTypes));
+ }
+
public List<String> getNullables() {
return myNullables;
}