[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
This commit is contained in:
Tagir Valeev
2020-12-14 12:03:52 +00:00
committed by intellij-monorepo-bot
parent 28047ca992
commit 2002634b32
4 changed files with 139 additions and 144 deletions
@@ -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<? extends PsiParameter> 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) {
@@ -218,13 +218,31 @@ public class AnnotationUtil {
@Nullable
public static PsiAnnotation findAnnotationInHierarchy(@NotNull final PsiModifierListOwner listOwner,
@NotNull Set<String> 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<String> 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;
@@ -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 : "") +
"}";
}
}
@@ -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<String> 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<String> 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<PsiParameter> 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<String> 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<String> 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<String> 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<PsiParameter> 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<? extends PsiParameter> 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<String> 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<String> 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<String> 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<String> 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);
}
/**