diff options
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.java | 72 |
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; } |