From 2002634b3245c63d3dc0f2aaab633ebe7f47f43b Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Mon, 14 Dec 2020 16:25:05 +0700 Subject: [PATCH] [java-analysis] NullableNotNullManager reworked Now, more methods go through findEffectiveNullabilityInfo, so their semantics is more aligned. Fixes IDEA-256427 Warning "Not annotated parameter overrides @??? parameter" GitOrigin-RevId: f00eeb27ea4690e477be161a3a018cd4d13c0e56 --- .../nullable/NullableStuffInspectionBase.java | 36 ++-- .../intellij/codeInsight/AnnotationUtil.java | 22 +- .../NullabilityAnnotationInfo.java | 25 ++- .../codeInsight/NullableNotNullManager.java | 200 +++++++----------- 4 files changed, 139 insertions(+), 144 deletions(-) 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 5ce56d24f3cb..60e3dd49705e 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 @@ -455,7 +455,7 @@ public class NullableStuffInspectionBase extends AbstractBaseJavaLocalInspection if (PropertyUtil.getFieldOfGetter(getter) == field) { LocalQuickFix getterAnnoFix = new AddAnnotationPsiFix(anno, getter, ArrayUtilRt.toStringArray(annoToRemove)); if (REPORT_NOT_ANNOTATED_GETTER) { - if (!manager.hasNullability(getter) && !TypeConversionUtil.isPrimitiveAndNotNull(getter.getReturnType())) { + if (!hasNullability(manager, getter) && !TypeConversionUtil.isPrimitiveAndNotNull(getter.getReturnType())) { holder.registerProblem(nameIdentifier, JavaAnalysisBundle .message("inspection.nullable.problems.annotated.field.getter.not.annotated", getPresentableAnnoName(field)), ProblemHighlightType.GENERIC_ERROR_OR_WARNING, getterAnnoFix); @@ -478,7 +478,7 @@ public class NullableStuffInspectionBase extends AbstractBaseJavaLocalInspection final PsiParameter parameter = parameters[0]; LOG.assertTrue(parameter != null, setter.getText()); AddAnnotationPsiFix addAnnoFix = createAddAnnotationFix(anno, annoToRemove, parameter); - if (REPORT_NOT_ANNOTATED_GETTER && !manager.hasNullability(parameter) && !TypeConversionUtil.isPrimitiveAndNotNull(parameter.getType())) { + if (REPORT_NOT_ANNOTATED_GETTER && !hasNullability(manager, parameter) && !TypeConversionUtil.isPrimitiveAndNotNull(parameter.getType())) { final PsiIdentifier parameterName = parameter.getNameIdentifier(); assertValidElement(setter, parameter, parameterName); holder.registerProblem(parameterName, @@ -528,7 +528,7 @@ public class NullableStuffInspectionBase extends AbstractBaseJavaLocalInspection PsiElement target = ((PsiReferenceExpression)rhs).resolve(); if (isConstructorParameter(target) && target.isPhysical()) { PsiParameter parameter = (PsiParameter)target; - if (REPORT_NOT_ANNOTATED_GETTER && !manager.hasNullability(parameter) && !TypeConversionUtil.isPrimitiveAndNotNull(parameter.getType())) { + if (REPORT_NOT_ANNOTATED_GETTER && !hasNullability(manager, parameter) && !TypeConversionUtil.isPrimitiveAndNotNull(parameter.getType())) { final PsiIdentifier nameIdentifier = parameter.getNameIdentifier(); if (nameIdentifier != null && nameIdentifier.isPhysical()) { holder.registerProblem( @@ -581,6 +581,15 @@ public class NullableStuffInspectionBase extends AbstractBaseJavaLocalInspection return StringUtil.getShortName(StringUtil.notNullize(annotation.getQualifiedName(), "???")); } + /** + * @return if owner has a @NotNull or @Nullable annotation, + * or is in scope of @ParametersAreNullableByDefault or ParametersAreNonnullByDefault + */ + private static boolean hasNullability(NullableNotNullManager manager, @NotNull PsiModifierListOwner owner) { + NullabilityAnnotationInfo info = manager.findEffectiveNullabilityInfo(owner); + return info != null && info.getNullability() != Nullability.UNKNOWN && info.getInheritedFrom() == null; + } + private static final class Annotated { private final boolean isDeclaredNotNull; private final boolean isDeclaredNullable; @@ -826,7 +835,7 @@ public class NullableStuffInspectionBase extends AbstractBaseJavaLocalInspection private PsiParameter findNotNullSuperForNonAnnotatedParameter(NullableNotNullManager nullableManager, PsiParameter parameter, List superParameters) { - return REPORT_NOT_ANNOTATED_METHOD_OVERRIDES_NOTNULL && !nullableManager.hasNullability(parameter) + return REPORT_NOT_ANNOTATED_METHOD_OVERRIDES_NOTNULL && !hasNullability(nullableManager, parameter) ? ContainerUtil.find(superParameters, sp -> isNotNullNotInferred(sp, false, IGNORE_EXTERNAL_SUPER_NOTNULL) && !hasInheritableNotNull(sp)) : null; @@ -845,7 +854,7 @@ public class NullableStuffInspectionBase extends AbstractBaseJavaLocalInspection if (!REPORT_NOTNULL_PARAMETERS_OVERRIDES_NOT_ANNOTATED) return false; NullabilityAnnotationInfo info = nullableManager.findOwnNullabilityInfo(parameter); return info != null && info.getNullability() == Nullability.NOT_NULL && !info.isInferred() && - ContainerUtil.exists(superParameters, sp -> !nullableManager.hasNullability(sp)); + ContainerUtil.exists(superParameters, sp -> !hasNullability(nullableManager, sp)); } private void checkNullLiteralArgumentOfNotNullParameterUsages(PsiMethod method, @@ -970,22 +979,19 @@ public class NullableStuffInspectionBase extends AbstractBaseJavaLocalInspection private static boolean isNotNullNotInferred(@NotNull PsiModifierListOwner owner, boolean checkBases, boolean skipExternal) { Project project = owner.getProject(); NullableNotNullManager manager = NullableNotNullManager.getInstance(project); - if (DfaPsiUtil.getTypeNullability(getMemberType(owner)) == Nullability.NOT_NULL) return true; - if (!manager.isNotNull(owner, checkBases)) return false; - - PsiAnnotation anno = manager.getNotNullAnnotation(owner, checkBases); - if (anno == null || AnnotationUtil.isInferredAnnotation(anno)) return false; - if (skipExternal && AnnotationUtil.isExternalAnnotation(anno)) return false; + NullabilityAnnotationInfo info = manager.findEffectiveNullabilityInfo(owner); + if (info == null || info.isInferred() || info.getNullability() != Nullability.NOT_NULL) return false; + if (!checkBases && info.getInheritedFrom() != null) return false; + if (skipExternal && info.isExternal()) return false; return true; } public static boolean isNullableNotInferred(@NotNull PsiModifierListOwner owner, boolean checkBases) { Project project = owner.getProject(); NullableNotNullManager manager = NullableNotNullManager.getInstance(project); - PsiAnnotation anno = manager.getNullableAnnotation(owner, checkBases); - if (anno == null) return false; - - return DfaPsiUtil.getTypeNullability(getMemberType(owner)) == Nullability.NULLABLE || !AnnotationUtil.isInferredAnnotation(anno); + NullabilityAnnotationInfo info = manager.findEffectiveNullabilityInfo(owner); + return info != null && !info.isInferred() && info.getNullability() == Nullability.NULLABLE && + (checkBases || info.getInheritedFrom() == null); } private static PsiType getMemberType(@NotNull PsiModifierListOwner owner) { diff --git a/java/java-psi-api/src/com/intellij/codeInsight/AnnotationUtil.java b/java/java-psi-api/src/com/intellij/codeInsight/AnnotationUtil.java index c60706f2c68f..e0d41c690614 100644 --- a/java/java-psi-api/src/com/intellij/codeInsight/AnnotationUtil.java +++ b/java/java-psi-api/src/com/intellij/codeInsight/AnnotationUtil.java @@ -218,13 +218,31 @@ public class AnnotationUtil { @Nullable public static PsiAnnotation findAnnotationInHierarchy(@NotNull final PsiModifierListOwner listOwner, @NotNull Set annotationNames, boolean skipExternal) { + AnnotationAndOwner result = findAnnotationAndOwnerInHierarchy(listOwner, annotationNames, skipExternal); + return result == null ? null : result.annotation; + } + + static final class AnnotationAndOwner { + final @NotNull PsiModifierListOwner owner; + final @NotNull PsiAnnotation annotation; + + AnnotationAndOwner(@NotNull PsiModifierListOwner owner, @NotNull PsiAnnotation annotation) { + this.owner = owner; + this.annotation = annotation; + } + } + + @Nullable + static AnnotationAndOwner findAnnotationAndOwnerInHierarchy(@NotNull final PsiModifierListOwner listOwner, + @NotNull Set annotationNames, + boolean skipExternal) { PsiAnnotation directAnnotation = findAnnotation(listOwner, annotationNames, skipExternal); - if (directAnnotation != null) return directAnnotation; + if (directAnnotation != null) return new AnnotationAndOwner(listOwner, directAnnotation); for (PsiModifierListOwner superOwner : getSuperAnnotationOwners(listOwner)) { PsiAnnotation annotation = findAnnotation(superOwner, annotationNames, skipExternal); if (annotation != null) { - return annotation; + return new AnnotationAndOwner(superOwner, annotation); } } return null; diff --git a/java/java-psi-api/src/com/intellij/codeInsight/NullabilityAnnotationInfo.java b/java/java-psi-api/src/com/intellij/codeInsight/NullabilityAnnotationInfo.java index e73f7d5e7d62..7c331632b503 100644 --- a/java/java-psi-api/src/com/intellij/codeInsight/NullabilityAnnotationInfo.java +++ b/java/java-psi-api/src/com/intellij/codeInsight/NullabilityAnnotationInfo.java @@ -2,7 +2,9 @@ package com.intellij.codeInsight; import com.intellij.psi.PsiAnnotation; +import com.intellij.psi.PsiModifierListOwner; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * Represents a particular nullability annotation instance @@ -10,11 +12,20 @@ import org.jetbrains.annotations.NotNull; public class NullabilityAnnotationInfo { private final @NotNull PsiAnnotation myAnnotation; private final @NotNull Nullability myNullability; + private final @Nullable PsiModifierListOwner myInheritedFrom; private final boolean myContainer; public NullabilityAnnotationInfo(@NotNull PsiAnnotation annotation, @NotNull Nullability nullability, boolean container) { + this(annotation, nullability, null, container); + } + + NullabilityAnnotationInfo(@NotNull PsiAnnotation annotation, + @NotNull Nullability nullability, + @Nullable PsiModifierListOwner inheritedFrom, + boolean container) { myAnnotation = annotation; myNullability = nullability; + myInheritedFrom = inheritedFrom; myContainer = container; } @@ -55,11 +66,23 @@ public class NullabilityAnnotationInfo { return AnnotationUtil.isInferredAnnotation(myAnnotation); } + /** + * @return an element the annotation was inherited from (PsiParameter or PsiMethod), or null if the annotation was not inherited. + */ + public @Nullable PsiModifierListOwner getInheritedFrom() { + return myInheritedFrom; + } + + NullabilityAnnotationInfo withInheritedFrom(PsiModifierListOwner owner) { + return new NullabilityAnnotationInfo(myAnnotation, myNullability, owner, myContainer); + } + @Override public String toString() { return "NullabilityAnnotationInfo{" + myNullability + "(" + myAnnotation.getQualifiedName() + ")" + - (myContainer ? ", container=" : "") + + (myContainer ? ", container" : "") + + (myInheritedFrom != null ? ", inherited from: " + myInheritedFrom : "") + "}"; } } 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 8ac00c1c130b..ca1281f8e59f 100644 --- a/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java +++ b/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java @@ -4,7 +4,6 @@ package com.intellij.codeInsight; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.RecursionManager; import com.intellij.psi.*; import com.intellij.psi.util.CachedValueProvider; import com.intellij.psi.util.CachedValuesManager; @@ -51,13 +50,6 @@ public abstract class NullableNotNullManager { return ServiceManager.getService(project, NullableNotNullManager.class); } - /** - * @return if owner has a @NotNull or @Nullable annotation, or is in scope of @ParametersAreNullableByDefault or ParametersAreNonnullByDefault - */ - public boolean hasNullability(@NotNull PsiModifierListOwner owner) { - return isNullable(owner, false) || isNotNull(owner, false); - } - public abstract void setNotNulls(String @NotNull ... annotations); public abstract void setNullables(String @NotNull ... annotations); @@ -65,11 +57,16 @@ public abstract class NullableNotNullManager { public abstract @NotNull String getDefaultNullable(); /** - * Returns an annotation which marks given element as Nullable, if any. Usage of this method is discouraged. - * Use {@link #findEffectiveNullabilityInfo(PsiModifierListOwner)} instead. + * Returns an annotation which marks given element as Nullable, if any. + * @deprecated use {@link #findEffectiveNullabilityInfo(PsiModifierListOwner)} instead. */ + @ApiStatus.ScheduledForRemoval(inVersion = "2021.3") + @Deprecated public @Nullable PsiAnnotation getNullableAnnotation(@NotNull PsiModifierListOwner owner, boolean checkBases) { - return findNullityAnnotationWithDefault(owner, checkBases, true); + NullabilityAnnotationInfo info = findEffectiveNullabilityInfo(owner); + if (info == null || info.getNullability() != Nullability.NULLABLE) return null; + if (!checkBases && info.getInheritedFrom() != null) return null; + return info.getAnnotation(); } public abstract void setDefaultNullable(@NotNull String defaultNullable); @@ -77,11 +74,16 @@ public abstract class NullableNotNullManager { public abstract @NotNull String getDefaultNotNull(); /** - * Returns an annotation which marks given element as NotNull, if any. Usage of this method is discouraged. - * Use {@link #findEffectiveNullabilityInfo(PsiModifierListOwner)} instead. + * Returns an annotation which marks given element as NotNull, if any. + * @deprecated use {@link #findEffectiveNullabilityInfo(PsiModifierListOwner)} instead. */ + @ApiStatus.ScheduledForRemoval(inVersion = "2021.3") + @Deprecated public @Nullable PsiAnnotation getNotNullAnnotation(@NotNull PsiModifierListOwner owner, boolean checkBases) { - return findNullityAnnotationWithDefault(owner, checkBases, false); + NullabilityAnnotationInfo info = findEffectiveNullabilityInfo(owner); + if (info == null || info.getNullability() != Nullability.NOT_NULL) return null; + if (!checkBases && info.getInheritedFrom() != null) return null; + return info.getAnnotation(); } public @Nullable PsiAnnotation copyNotNullAnnotation(@NotNull PsiModifierListOwner original, @NotNull PsiModifierListOwner generated) { @@ -124,58 +126,8 @@ public abstract class NullableNotNullManager { return null; } - /** @deprecated use {@link #copyNotNullAnnotation(PsiModifierListOwner, PsiModifierListOwner)} */ - @Deprecated - @ApiStatus.ScheduledForRemoval(inVersion = "2021.1") - public PsiAnnotation copyNotNullAnnotation(@NotNull PsiModifierListOwner owner) { - NullabilityAnnotationInfo info = findOwnNullabilityInfo(owner); - if (info == null || info.getNullability() != Nullability.NOT_NULL) return null; - String qualifiedName = info.getAnnotation().getQualifiedName(); - return qualifiedName != null - ? JavaPsiFacade.getElementFactory(owner.getProject()).createAnnotationFromText("@" + qualifiedName, owner) - : null; - } - public abstract void setDefaultNotNull(@NotNull String defaultNotNull); - private @Nullable PsiAnnotation findNullityAnnotationWithDefault(@NotNull PsiModifierListOwner owner, boolean checkBases, boolean nullable) { - PsiAnnotation annotation = findPlainNullityAnnotation(owner, checkBases); - if (annotation != null) { - String qName = annotation.getQualifiedName(); - if (qName == null) return null; - - List contradictory = nullable ? getNotNullsWithNickNames() : getNullablesWithNickNames(); - if (contradictory.contains(qName)) return null; - - return annotation; - } - - 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 - List annotations = nullable ? getDefaultNotNulls() : getDefaultNullables(); - int flags = (checkBases ? CHECK_HIERARCHY : 0) | CHECK_EXTERNAL | CHECK_INFERRED | CHECK_TYPE; - if (isAnnotated(owner, annotations, flags)) { - return null; - } - - if (!nullable && hasHardcodedContracts(owner)) { - return null; - } - - if (owner instanceof PsiParameter && !nullable && checkBases) { - List superParameters = getSuperAnnotationOwners((PsiParameter)owner); - if (!superParameters.isEmpty()) { - return takeAnnotationFromSuperParameters((PsiParameter)owner, superParameters); - } - } - - NullabilityAnnotationInfo nullityDefault = findNullityDefaultInHierarchy(owner); - Nullability wantedNullability = nullable ? Nullability.NULLABLE : Nullability.NOT_NULL; - return nullityDefault != null && nullityDefault.getNullability() == wantedNullability ? nullityDefault.getAnnotation() : null; - } - /** * Returns own nullability annotation info for given element. Returned annotation is not inherited and * not container annotation for class/package. Still it could be inferred or external. @@ -184,17 +136,9 @@ public abstract class NullableNotNullManager { * @return own nullability annotation info. */ public @Nullable NullabilityAnnotationInfo findOwnNullabilityInfo(@NotNull PsiModifierListOwner owner) { - PsiType type = getOwnerType(owner); - if (type == null || TypeConversionUtil.isPrimitiveAndNotNull(type)) return null; - - List nullables = getNullablesWithNickNames(); - PsiAnnotation annotation = findPlainNullityAnnotation(owner, false); - if (annotation != null) { - return new NullabilityAnnotationInfo(annotation, - nullables.contains(annotation.getQualifiedName()) ? Nullability.NULLABLE : Nullability.NOT_NULL, - false); - } - return null; + NullabilityAnnotationInfo info = findEffectiveNullabilityInfo(owner); + if (info == null || info.isContainer() || info.getInheritedFrom() != null) return null; + return info; } /** @@ -206,8 +150,9 @@ public abstract class NullableNotNullManager { * @return the annotation info or null if no explicit annotation found */ public @Nullable NullabilityAnnotationInfo findExplicitNullability(PsiModifierListOwner owner) { - PsiAnnotation annotation = findPlainAnnotation(owner, getAllNullabilityAnnotationsWithNickNames(), false, true); - if (annotation != null) { + AnnotationAndOwner result = findPlainAnnotation(owner, getAllNullabilityAnnotationsWithNickNames(), false, true); + if (result != null) { + PsiAnnotation annotation = result.annotation; Nullability nullability = getNullablesWithNickNames().contains(annotation.getQualifiedName()) ? Nullability.NULLABLE : Nullability.NOT_NULL; return new NullabilityAnnotationInfo(annotation, nullability, false); @@ -234,57 +179,56 @@ public abstract class NullableNotNullManager { Set extraAnnotations = new HashSet<>(getAllDefaultAnnotations()); extraAnnotations.addAll(annotationNames); - PsiAnnotation annotation = findPlainAnnotation(owner, extraAnnotations, true, false); - if (annotation != null) { - if (!annotationNames.contains(annotation.getQualifiedName())) { + AnnotationAndOwner result = findPlainAnnotation(owner, extraAnnotations, true, false); + if (result != null) { + if (!annotationNames.contains(result.annotation.getQualifiedName())) { // Deliberately excluded known standard annotation still has precedence over default class-level or package-level annotation: // return null in this case return null; } List nullables = getNullablesWithNickNames(); - return new NullabilityAnnotationInfo(annotation, - nullables.contains(annotation.getQualifiedName()) ? Nullability.NULLABLE : Nullability.NOT_NULL, + return new NullabilityAnnotationInfo(result.annotation, + nullables.contains(result.annotation.getQualifiedName()) ? Nullability.NULLABLE : Nullability.NOT_NULL, + result.owner == owner ? null : result.owner, false); } + boolean lambdaParameter = owner instanceof PsiParameter && owner.getParent() instanceof PsiParameterList && + owner.getParent().getParent() instanceof PsiLambdaExpression; + + if (!lambdaParameter) { + // For lambda parameter, inherited annotation overrides the default one + NullabilityAnnotationInfo defaultInfo = findNullityDefaultInHierarchy(owner); + if (defaultInfo != null && (defaultInfo.getNullability() == Nullability.NULLABLE || !hasHardcodedContracts(owner))) { + return defaultInfo; + } + } + if (owner instanceof PsiParameter) { List superParameters = getSuperAnnotationOwners((PsiParameter)owner); if (!superParameters.isEmpty()) { for (PsiParameter parameter: superParameters) { - PsiAnnotation plain = findPlainAnnotation(parameter, extraAnnotations, false, false); + AnnotationAndOwner plain = findPlainAnnotation(parameter, extraAnnotations, false, false); // Plain not null annotation is not inherited if (plain != null) return null; NullabilityAnnotationInfo defaultInfo = findNullityDefaultInHierarchy(parameter); if (defaultInfo != null) { - return defaultInfo.getNullability() == Nullability.NOT_NULL ? defaultInfo : null; + return defaultInfo.getNullability() == Nullability.NOT_NULL ? defaultInfo.withInheritedFrom(parameter) : null; } } return null; } } - NullabilityAnnotationInfo defaultInfo = findNullityDefaultInHierarchy(owner); - if (defaultInfo != null && (defaultInfo.getNullability() == Nullability.NULLABLE || !hasHardcodedContracts(owner))) { - return defaultInfo; + if (lambdaParameter) { + NullabilityAnnotationInfo defaultInfo = findNullityDefaultInHierarchy(owner); + if (defaultInfo != null && (defaultInfo.getNullability() == Nullability.NULLABLE || !hasHardcodedContracts(owner))) { + return defaultInfo; + } } return null; } - private PsiAnnotation takeAnnotationFromSuperParameters(@NotNull PsiParameter owner, @NotNull List superOwners) { - return RecursionManager.doPreventingRecursion(owner, true, () -> { - for (PsiParameter superOwner : superOwners) { - PsiAnnotation anno = findNullityAnnotationWithDefault(superOwner, false, false); - if (anno != null) return anno; - } - return null; - }); - } - - private PsiAnnotation findPlainNullityAnnotation(@NotNull PsiModifierListOwner owner, boolean checkBases) { - Set qNames = getAllNullabilityAnnotationsWithNickNames(); - return findPlainAnnotation(owner, qNames, checkBases, false); - } - /** * @return an annotation (if any) with the given nullability semantics on the given declaration or its type. In case of conflicts, * type annotations are preferred. @@ -292,39 +236,41 @@ public abstract class NullableNotNullManager { public @Nullable PsiAnnotation findExplicitNullabilityAnnotation(@NotNull PsiModifierListOwner owner, @NotNull Nullability nullability) { if (nullability == Nullability.UNKNOWN) return null; List names = nullability == Nullability.NULLABLE ? getNullablesWithNickNames() : getNotNullsWithNickNames(); - return findPlainAnnotation(owner, new HashSet<>(names), false, false); + AnnotationAndOwner result = findPlainAnnotation(owner, new HashSet<>(names), false, false); + return result == null ? null : result.annotation; } - private static @Nullable PsiAnnotation findPlainAnnotation(@NotNull PsiModifierListOwner owner, - @NotNull Set qualifiedNames, boolean checkBases, boolean skipExternal) { - PsiAnnotation memberAnno = checkBases && owner instanceof PsiMethod - ? findAnnotationInHierarchy(owner, qualifiedNames, skipExternal) - : findAnnotation(owner, qualifiedNames, skipExternal); + private static @Nullable AnnotationAndOwner findPlainAnnotation( + @NotNull PsiModifierListOwner owner, @NotNull Set qualifiedNames, boolean checkBases, boolean skipExternal) { + AnnotationAndOwner memberAnno; + if (checkBases && owner instanceof PsiMethod) { + memberAnno = findAnnotationAndOwnerInHierarchy(owner, qualifiedNames, skipExternal); + } + else { + PsiAnnotation annotation = findAnnotation(owner, qualifiedNames, skipExternal); + memberAnno = annotation == null ? null : new AnnotationAndOwner(owner, annotation); + } PsiType type = getOwnerType(owner); - if (memberAnno != null && type instanceof PsiArrayType && !isInferredAnnotation(memberAnno) && !isExternalAnnotation(memberAnno) && - AnnotationTargetUtil.isTypeAnnotation(memberAnno)) { + if (memberAnno != null && type instanceof PsiArrayType && !isInferredAnnotation(memberAnno.annotation) && + !isExternalAnnotation(memberAnno.annotation) && AnnotationTargetUtil.isTypeAnnotation(memberAnno.annotation)) { // Ambiguous TYPE_USE annotation on array type: we consider that it annotates an array component instead. // ignore inferred/external annotations here, as they are applicable to PsiModifierListOwner only, regardless of target memberAnno = null; } if (memberAnno != null) { - PsiAnnotation annotation = preferTypeAnnotation(memberAnno, type); - if (annotation != memberAnno && !qualifiedNames.contains(annotation.getQualifiedName())) return null; - return annotation; - } - if (type instanceof PsiPrimitiveType) return null; - return findAnnotationInTypeHierarchy(type, qualifiedNames); - } - - private static @NotNull PsiAnnotation preferTypeAnnotation(@NotNull PsiAnnotation memberAnno, @Nullable PsiType type) { - if (type != null) { - for (PsiAnnotation typeAnno : type.getApplicableAnnotations()) { - if (areDifferentNullityAnnotations(memberAnno, typeAnno)) { - return typeAnno; + if (type != null) { + for (PsiAnnotation typeAnno : type.getApplicableAnnotations()) { + if (areDifferentNullityAnnotations(memberAnno.annotation, typeAnno)) { + if (typeAnno != memberAnno.annotation && !qualifiedNames.contains(typeAnno.getQualifiedName())) return null; + return new AnnotationAndOwner(owner, typeAnno); + } } } + return memberAnno; } - return memberAnno; + if (type instanceof PsiPrimitiveType) return null; + PsiAnnotation annotationFromType = findAnnotationInTypeHierarchy(type, qualifiedNames); + return annotationFromType == null ? null : new AnnotationAndOwner(owner, annotationFromType); } private static boolean areDifferentNullityAnnotations(@NotNull PsiAnnotation memberAnno, @NotNull PsiAnnotation typeAnno) { @@ -360,11 +306,13 @@ public abstract class NullableNotNullManager { } public boolean isNullable(@NotNull PsiModifierListOwner owner, boolean checkBases) { - return findNullityAnnotationWithDefault(owner, checkBases, true) != null; + NullabilityAnnotationInfo info = findEffectiveNullabilityInfo(owner); + return info != null && info.getNullability() == Nullability.NULLABLE && (checkBases || info.getInheritedFrom() == null); } public boolean isNotNull(@NotNull PsiModifierListOwner owner, boolean checkBases) { - return findNullityAnnotationWithDefault(owner, checkBases, false) != null; + NullabilityAnnotationInfo info = findEffectiveNullabilityInfo(owner); + return info != null && info.getNullability() == Nullability.NOT_NULL && (checkBases || info.getInheritedFrom() == null); } /**