mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[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:
committed by
intellij-monorepo-bot
parent
28047ca992
commit
2002634b32
+21
-15
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user