[java-dfa] IDEA-358087 Local variable nullability annotations are ignored with default qualifier

GitOrigin-RevId: e640517083910df691b58415f7ba706be37223ec
This commit is contained in:
Tagir Valeev
2025-03-18 14:53:44 +00:00
committed by intellij-monorepo-bot
parent af361ef042
commit c4e72cac0b
3 changed files with 44 additions and 3 deletions
@@ -7,6 +7,7 @@ import com.intellij.openapi.util.Ref;
import com.intellij.psi.*;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.ObjectUtils;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -262,8 +263,13 @@ public abstract class NullableNotNullManager {
return new NullabilityAnnotationInfo(memberAnno.annotation, nullability, memberAnno.owner == owner ? null : memberAnno.owner, false);
}
if (type instanceof PsiPrimitiveType) return null;
if (owner instanceof PsiLocalVariable) return null;
return findAnnotationInTypeHierarchy(type, annotations);
NullabilityAnnotationInfo inHierarchy = findAnnotationInTypeHierarchy(type, annotations);
if (inHierarchy != null &&
owner instanceof PsiLocalVariable &&
!canAnnotateLocals(inHierarchy.getAnnotation().getQualifiedName())) {
return null;
}
return inHierarchy;
}
protected @NotNull Nullability correctNullability(@NotNull Nullability nullability, @NotNull PsiAnnotation annotation) {
@@ -435,10 +441,18 @@ public abstract class NullableNotNullManager {
}
}
if (!(eachType instanceof PsiClassType)) return true;
PsiClassType classType = (PsiClassType)eachType;
PsiClass targetClass = PsiUtil.resolveClassInClassTypeOnly(eachType);
if (!(targetClass instanceof PsiTypeParameter)) return false;
if (targetClass.getExtendsListTypes().length == 0) {
NullabilityAnnotationInfo info = findNullabilityDefault(targetClass, PsiAnnotation.TargetType.TYPE_PARAMETER);
PsiAnnotation.TargetType[] targetType;
PsiModifierListOwner owner = getOwner(classType);
if (owner != null) {
targetType = AnnotationTargetUtil.getTargetsForLocation(owner.getModifierList());
} else {
targetType = new PsiAnnotation.TargetType[]{PsiAnnotation.TargetType.TYPE_PARAMETER};
}
NullabilityAnnotationInfo info = findNullabilityDefault(targetClass, targetType);
if (info != null) {
result.set(info);
return false;
@@ -448,6 +462,18 @@ public abstract class NullableNotNullManager {
});
return result.get();
}
private static PsiModifierListOwner getOwner(@NotNull PsiClassType classType) {
PsiJavaCodeReferenceElement context = ObjectUtils.tryCast(classType.getPsiContext(), PsiJavaCodeReferenceElement.class);
if (context != null) {
PsiTypeElement typeElement = ObjectUtils.tryCast(context.getParent(), PsiTypeElement.class);
if (typeElement != null) {
PsiModifierListOwner owner = ObjectUtils.tryCast(typeElement.getParent(), PsiModifierListOwner.class);
return owner;
}
}
return null;
}
protected interface NullabilityAnnotationDataHolder {
/**
@@ -0,0 +1,10 @@
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.framework.qual.DefaultQualifier;
@DefaultQualifier(NonNull.class)
final class Main {
public static void main(final String[] args) {
final @Nullable String foo = null;
}
}
@@ -47,6 +47,11 @@ public class DataFlowInspection16Test extends DataFlowInspectionTestCase {
doTest();
}
public void testCheckerLocalVariableOverridesDefault() {
addCheckerAnnotations(myFixture);
doTest();
}
public void testRecordComponentAnnotate() {
doTest();
IntentionAction intention = myFixture.findSingleIntention("Annotate record component 'member' as '@Nullable'");