From aafd05ff0c8eb74451eb7dd102337a11cfd2401b Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Wed, 2 Apr 2025 18:55:19 +0200 Subject: [PATCH] [java-inspections] When analyzing annotation conflict, ignore container annotation if non-container is present Fixes IDEA-369220 JSpecify support - wrong warning when Nullable method is declared in NullMarked scope and overridden GitOrigin-RevId: 50163bfda72bd1f583dda644c668b78859fac022 --- .../nullable/NullableStuffInspectionBase.java | 12 +++++++++--- .../codeInsight/NullableNotNullManager.java | 8 ++++---- .../OverriddenWithNullMarked.java | 18 ++++++++++++++++++ .../NullableStuffInspectionTest.java | 7 +++++++ 4 files changed, 38 insertions(+), 7 deletions(-) create mode 100644 java/java-tests/testData/inspection/nullableProblems/OverriddenWithNullMarked.java diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/nullable/NullableStuffInspectionBase.java b/java/java-analysis-impl/src/com/intellij/codeInspection/nullable/NullableStuffInspectionBase.java index 99dc82387ed9..2180a09a212d 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/nullable/NullableStuffInspectionBase.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/nullable/NullableStuffInspectionBase.java @@ -264,8 +264,9 @@ public class NullableStuffInspectionBase extends AbstractBaseJavaLocalInspection anno != annotation && manager.getAnnotationNullability(anno.getQualifiedName()).filter(n -> n != nullability).isPresent(); PsiAnnotation oppositeAnno = ContainerUtil.find(owner.getAnnotations(), filter); if (oppositeAnno == null && listOwner != null) { - oppositeAnno = manager.findExplicitNullabilityAnnotation( + NullabilityAnnotationInfo result = manager.findNullabilityAnnotationInfo( listOwner, ContainerUtil.filter(Nullability.values(), n -> n != nullability)); + oppositeAnno = result == null || result.isContainer() ? null : result.getAnnotation(); } if (oppositeAnno != null && Objects.equals(getRelatedType(annotation), getRelatedType(oppositeAnno))) { @@ -700,8 +701,13 @@ public class NullableStuffInspectionBase extends AbstractBaseJavaLocalInspection static @NotNull Annotated from(@NotNull PsiModifierListOwner owner) { NullableNotNullManager manager = NullableNotNullManager.getInstance(owner.getProject()); - return new Annotated(manager.findExplicitNullabilityAnnotation(owner, Collections.singleton(Nullability.NOT_NULL)), - manager.findExplicitNullabilityAnnotation(owner, Collections.singleton(Nullability.NULLABLE))); + NullabilityAnnotationInfo notNullInfo = manager.findNullabilityAnnotationInfo(owner, Collections.singleton(Nullability.NOT_NULL)); + NullabilityAnnotationInfo nullableInfo = manager.findNullabilityAnnotationInfo(owner, Collections.singleton(Nullability.NULLABLE)); + PsiAnnotation nullableAnno = notNullInfo == null || (notNullInfo.isContainer() && nullableInfo != null && !nullableInfo.isContainer()) + ? null : notNullInfo.getAnnotation(); + PsiAnnotation notNullAnno = nullableInfo == null || (nullableInfo.isContainer() && notNullInfo != null && !notNullInfo.isContainer()) + ? null : nullableInfo.getAnnotation(); + return new Annotated(nullableAnno, notNullAnno); } } diff --git a/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java b/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java index 7dad00de0e14..99081b4ef8a1 100644 --- a/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java +++ b/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java @@ -208,11 +208,11 @@ public abstract class NullableNotNullManager { } /** - * @return an annotation (if any) with the given nullability semantics on the given declaration or its type. In case of conflicts, + * @return the annotation info (if any) with the given nullability semantics on the given declaration or its type. In case of conflicts, * type annotations are preferred. */ - public @Nullable PsiAnnotation findExplicitNullabilityAnnotation(@NotNull PsiModifierListOwner owner, - @NotNull Collection nullabilities) { + public @Nullable NullabilityAnnotationInfo findNullabilityAnnotationInfo(@NotNull PsiModifierListOwner owner, + @NotNull Collection nullabilities) { NullabilityAnnotationDataHolder holder = getAllNullabilityAnnotationsWithNickNames(); Set filteredSet = holder.qualifiedNames().stream().filter(qName -> nullabilities.contains(holder.getNullability(qName))).collect(Collectors.toSet()); @@ -229,7 +229,7 @@ public abstract class NullableNotNullManager { } }; NullabilityAnnotationInfo result = findPlainAnnotation(owner, false, filtered); - return result == null || !nullabilities.contains(result.getNullability()) ? null : result.getAnnotation(); + return result == null || !nullabilities.contains(result.getNullability()) ? null : result; } private @Nullable NullabilityAnnotationInfo findPlainAnnotation( diff --git a/java/java-tests/testData/inspection/nullableProblems/OverriddenWithNullMarked.java b/java/java-tests/testData/inspection/nullableProblems/OverriddenWithNullMarked.java new file mode 100644 index 000000000000..b8ccb93f59c9 --- /dev/null +++ b/java/java-tests/testData/inspection/nullableProblems/OverriddenWithNullMarked.java @@ -0,0 +1,18 @@ +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +class Main { + @NullMarked + interface Api { + @Nullable + T call(); + } + + @NullMarked + static class Impl implements Api { + @Nullable + public T call() { + return null; + } + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/codeInspection/NullableStuffInspectionTest.java b/java/java-tests/testSrc/com/intellij/java/codeInspection/NullableStuffInspectionTest.java index 70c82a9107d2..09786cb45d3d 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInspection/NullableStuffInspectionTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInspection/NullableStuffInspectionTest.java @@ -422,6 +422,13 @@ public class NullableStuffInspectionTest extends LightJavaCodeInsightFixtureTest doTest(); } + public void testOverriddenWithNullMarked() { + myInspection.REPORT_ANNOTATION_NOT_PROPAGATED_TO_OVERRIDERS = true; + DataFlowInspectionTestCase.addJSpecifyNullMarked(myFixture); + DataFlowInspectionTestCase.setupTypeUseAnnotations("org.jspecify.annotations", myFixture); + doTest(); + } + public void testNullableParameterOverride() { doTest(); }