mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[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:
committed by
intellij-monorepo-bot
parent
9d6899a508
commit
86d0145b26
@@ -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();
|
||||
}
|
||||
|
||||
+1
-1
@@ -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;" +
|
||||
|
||||
Reference in New Issue
Block a user