[java-dfa] IDEA-351380 Support @CheckForNull with @TypeQualifierNickname

GitOrigin-RevId: 7247332e3e0aba95116cb4d84696c00cfa20ba45
This commit is contained in:
Tagir Valeev
2024-04-19 09:51:46 +00:00
committed by intellij-monorepo-bot
parent a116699350
commit 02f815ca4a
3 changed files with 64 additions and 14 deletions
@@ -16,6 +16,7 @@ import java.util.Set;
public final class Jsr305Support implements AnnotationPackageSupport {
public static final String JAVAX_ANNOTATION_NULLABLE = "javax.annotation.Nullable";
public static final String JAVAX_ANNOTATION_NONNULL = "javax.annotation.Nonnull";
public static final String JAVAX_ANNOTATION_CHECK_FOR_NULL = "javax.annotation.CheckForNull";
public static final String TYPE_QUALIFIER_NICKNAME = "javax.annotation.meta.TypeQualifierNickname";
@Override
@@ -69,10 +70,18 @@ public final class Jsr305Support implements AnnotationPackageSupport {
* @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;
PsiModifierList modifierList = psiClass.getModifierList();
if (modifierList == null) return null;
if (!modifierList.hasAnnotation(TYPE_QUALIFIER_NICKNAME)) return null;
PsiAnnotation nonNull = AnnotationUtil.findAnnotation(psiClass, JAVAX_ANNOTATION_NONNULL);
return nonNull != null ? extractNullityFromWhenValue(nonNull) : null;
PsiAnnotation nonNull = modifierList.findAnnotation(JAVAX_ANNOTATION_NONNULL);
if (nonNull != null) {
return extractNullityFromWhenValue(nonNull);
}
if (modifierList.hasAnnotation(JAVAX_ANNOTATION_CHECK_FOR_NULL)) {
return Nullability.NULLABLE;
}
return null;
}
public static @Nullable Nullability extractNullityFromWhenValue(@NotNull PsiAnnotation nonNull) {
@@ -101,7 +110,7 @@ public final class Jsr305Support implements AnnotationPackageSupport {
public @NotNull List<String> getNullabilityAnnotations(@NotNull Nullability nullability) {
return switch (nullability) {
case NOT_NULL -> Collections.singletonList(JAVAX_ANNOTATION_NONNULL);
case NULLABLE -> Arrays.asList(JAVAX_ANNOTATION_NULLABLE, "javax.annotation.CheckForNull");
case NULLABLE -> Arrays.asList(JAVAX_ANNOTATION_NULLABLE, JAVAX_ANNOTATION_CHECK_FOR_NULL);
case UNKNOWN -> Collections.emptyList();
};
}
@@ -0,0 +1,27 @@
import static javax.annotation.meta.When.MAYBE;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import javax.annotation.meta.TypeQualifierNickname;
class Main {
int go(Lib lib) {
return lib.usingNonnullMaybe().<warning descr="Method invocation 'hashCode' may produce 'NullPointerException'">hashCode</warning>() + lib.usingCheckForNull().<warning descr="Method invocation 'hashCode' may produce 'NullPointerException'">hashCode</warning>();
}
interface Lib {
@UsingNonnullMaybe
Object usingNonnullMaybe();
@UsingCheckForNull
Object usingCheckForNull();
}
@Nonnull(when = MAYBE)
@TypeQualifierNickname
@interface UsingNonnullMaybe {}
@CheckForNull
@TypeQualifierNickname
@interface UsingCheckForNull {}
}
@@ -352,6 +352,12 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase {
doTest();
}
public void testJsr305CheckForNullAsQualifierNickname() {
addJavaxNullabilityAnnotations(myFixture);
addJavaxDefaultNullabilityAnnotations(myFixture);
doTest();
}
public void testNullabilityDefaultVsMethodImplementing() {
addJavaxDefaultNullabilityAnnotations(myFixture);
@@ -396,16 +402,24 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase {
fixture.addClass("package javax.annotation.meta;" +
"public enum When { ALWAYS, UNKNOWN, MAYBE, NEVER }");
fixture.addClass("package javax.annotation;" +
"import javax.annotation.meta.*;" +
"public @interface Nonnull {" +
" When when() default When.ALWAYS;" +
"}");
fixture.addClass("package javax.annotation;" +
"import javax.annotation.meta.*;" +
"@TypeQualifierNickname " +
"@Nonnull(when = When.UNKNOWN) " +
"public @interface Nullable {}");
fixture.addClass("""
package javax.annotation;
import javax.annotation.meta.*;
public @interface Nonnull {
When when() default When.ALWAYS;
}""");
fixture.addClass("""
package javax.annotation;
import javax.annotation.meta.*;
@TypeQualifierNickname
@Nonnull(when = When.MAYBE)
public @interface CheckForNull {}""");
fixture.addClass("""
package javax.annotation;
import javax.annotation.meta.*;
@TypeQualifierNickname
@Nonnull(when = When.UNKNOWN)
public @interface Nullable {}""");
}
public void testCustomTypeQualifierDefault() {