[java-dfa] Support JSR-305 unknown nullability

Fixes IDEA-278618 JSR-305 @Nonnull(when = UNKNOWN) not handled correctly

GitOrigin-RevId: 5983fbdcaad42dd5db8e1c8adb27cedbb9a746e2
This commit is contained in:
Tagir Valeev
2021-09-24 05:54:17 +00:00
committed by intellij-monorepo-bot
parent 9d6899a508
commit 86d0145b26
6 changed files with 65 additions and 11 deletions
@@ -399,6 +399,17 @@ public class NullableNotNullManagerImpl extends NullableNotNullManager implement
});
}
@Override
protected @NotNull Nullability correctNullability(@NotNull Nullability nullability, @NotNull PsiAnnotation annotation) {
if (nullability == Nullability.NOT_NULL && annotation.hasQualifiedName(Jsr305Support.JAVAX_ANNOTATION_NONNULL)) {
Nullability correctedNullability = Jsr305Support.extractNullityFromWhenValue(annotation);
if (correctedNullability != null) {
return correctedNullability;
}
}
return nullability;
}
@Override
public long getModificationCount() {
return myTracker.getModificationCount();
@@ -59,19 +59,21 @@ public class Jsr305Support implements AnnotationPackageSupport {
public static boolean isNullabilityNickName(@NotNull PsiClass candidate) {
String qname = candidate.getQualifiedName();
if (qname == null || qname.startsWith("javax.annotation.")) return false;
return getNickNamedNullability(candidate) != Nullability.UNKNOWN;
return getNickNamedNullability(candidate) != null;
}
@NotNull
public static Nullability getNickNamedNullability(@NotNull PsiClass psiClass) {
if (AnnotationUtil.findAnnotation(psiClass, TYPE_QUALIFIER_NICKNAME) == null) return Nullability.UNKNOWN;
/**
* @param psiClass annotation class
* @return nicknamed nullability declared by this annotation; null if this annotation is not a nullability nickname annotation
*/
public static @Nullable Nullability getNickNamedNullability(@NotNull PsiClass psiClass) {
if (AnnotationUtil.findAnnotation(psiClass, TYPE_QUALIFIER_NICKNAME) == null) return null;
PsiAnnotation nonNull = AnnotationUtil.findAnnotation(psiClass, JAVAX_ANNOTATION_NONNULL);
return nonNull != null ? extractNullityFromWhenValue(nonNull) : Nullability.UNKNOWN;
return nonNull != null ? extractNullityFromWhenValue(nonNull) : null;
}
@NotNull
private static Nullability extractNullityFromWhenValue(@NotNull PsiAnnotation nonNull) {
public static @Nullable Nullability extractNullityFromWhenValue(@NotNull PsiAnnotation nonNull) {
PsiAnnotationMemberValue when = nonNull.findAttributeValue("when");
if (when instanceof PsiReferenceExpression) {
String refName = ((PsiReferenceExpression)when).getReferenceName();
@@ -81,13 +83,16 @@ public class Jsr305Support implements AnnotationPackageSupport {
if ("MAYBE".equals(refName) || "NEVER".equals(refName)) {
return Nullability.NULLABLE;
}
if ("UNKNOWN".equals(refName)) {
return Nullability.UNKNOWN;
}
}
// 'when' is unknown and annotation is known -> default value (for javax.annotation.Nonnull is ALWAYS)
if (when == null && JAVAX_ANNOTATION_NONNULL.equals(nonNull.getQualifiedName())) {
if (when == null) {
return Nullability.NOT_NULL;
}
return Nullability.UNKNOWN;
return null;
}
@NotNull
@@ -237,7 +237,7 @@ public abstract class NullableNotNullManager {
}
};
NullabilityAnnotationInfo result = findPlainAnnotation(owner, false, false, filtered);
return result == null ? null : result.getAnnotation();
return result == null || !nullabilities.contains(result.getNullability()) ? null : result.getAnnotation();
}
private @Nullable NullabilityAnnotationInfo findPlainAnnotation(
@@ -260,6 +260,7 @@ public abstract class NullableNotNullManager {
if (memberAnno != null) {
Nullability nullability = annotations.getNullability(memberAnno.annotation.getQualifiedName());
if (nullability == null) return null;
nullability = correctNullability(nullability, memberAnno.annotation);
if (type != null) {
for (PsiAnnotation typeAnno : type.getApplicableAnnotations()) {
if (typeAnno == memberAnno.annotation) continue;
@@ -279,6 +280,10 @@ public abstract class NullableNotNullManager {
return findAnnotationInTypeHierarchy(type, annotations);
}
protected @NotNull Nullability correctNullability(@NotNull Nullability nullability, @NotNull PsiAnnotation annotation) {
return nullability;
}
private static boolean areDifferentNullityAnnotations(@NotNull PsiAnnotation memberAnno, @NotNull PsiAnnotation typeAnno) {
NullableNotNullManager manager = getInstance(memberAnno.getProject());
List<String> notNulls = manager.getNotNullsWithNickNames();
@@ -437,6 +442,7 @@ public abstract class NullableNotNullManager {
if (qualifiedNames.qualifiedNames().contains(qualifiedName)) {
Nullability nullability = qualifiedNames.getNullability(qualifiedName);
if (nullability != null) {
nullability = correctNullability(nullability, annotation);
result.set(new NullabilityAnnotationInfo(annotation, nullability, false));
}
return false;
@@ -0,0 +1,26 @@
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import javax.annotation.meta.TypeQualifierNickname;
import javax.annotation.meta.When;
@ParametersAreNonnullByDefault
final class Test<T> {
@Nonnull(when = When.UNKNOWN)
@TypeQualifierNickname
@interface UnknownNullity {}
@UnknownNullity
private final T value;
@Nonnull(when = When.UNKNOWN)
private final T value2;
public Test(@UnknownNullity T value, @Nonnull(when = When.UNKNOWN) T value2) {
this.value = value;
this.value2 = value2;
}
public boolean isNull() {
return value == null || value2 == null;
}
}
@@ -121,6 +121,12 @@ public class DataFlowInspection8Test extends DataFlowInspectionTestCase {
doTest();
}
public void testNonNullWhenUnknown() {
DataFlowInspectionTest.addJavaxNullabilityAnnotations(myFixture);
DataFlowInspectionTest.addJavaxDefaultNullabilityAnnotations(myFixture);
doTest();
}
public void testReturningNullFromTypeAnnotatedNullableMethod() {
doTestWithCustomAnnotations();
}
@@ -354,7 +354,6 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase {
}
public void testTypeQualifierNickname() {
myFixture.addClass("package javax.annotation.meta; public @interface TypeQualifierNickname {}");
addJavaxNullabilityAnnotations(myFixture);
myFixture.addClass(barNullableNick());
@@ -386,6 +385,7 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase {
}
public static void addJavaxNullabilityAnnotations(final JavaCodeInsightTestFixture fixture) {
fixture.addClass("package javax.annotation.meta; public @interface TypeQualifierNickname {}");
fixture.addClass("package javax.annotation.meta;" +
"public @interface TypeQualifierDefault { java.lang.annotation.ElementType[] value() default {};}");
fixture.addClass("package javax.annotation.meta;" +