From 06ae5648d1c0b6434daf4039204368a8d686ee9a Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Fri, 2 May 2025 12:40:58 +0200 Subject: [PATCH] [java-inspections] IDEA-371907 Warn about redundant '@NotNull' annotation when the scope is already covered by '@NotNullByDefault' GitOrigin-RevId: a703f9e76d11471607532063b4c6c55433579080 --- .../messages/JavaAnalysisBundle.properties | 1 + .../nullable/NullableStuffInspectionBase.java | 19 +++++++++++++++++++ .../nullableProblems/RedundantNotNull.java | 17 +++++++++++++++++ .../NullableStuffInspectionTest.java | 5 +++++ 4 files changed, 42 insertions(+) create mode 100644 java/java-tests/testData/inspection/nullableProblems/RedundantNotNull.java diff --git a/java/java-analysis-api/resources/messages/JavaAnalysisBundle.properties b/java/java-analysis-api/resources/messages/JavaAnalysisBundle.properties index ced422a7d541..24d3395bf715 100644 --- a/java/java-analysis-api/resources/messages/JavaAnalysisBundle.properties +++ b/java/java-analysis-api/resources/messages/JavaAnalysisBundle.properties @@ -240,6 +240,7 @@ inspection.nullable.problems.annotated.field.setter.parameter.not.annotated=Sett inspection.nullable.problems.method.overrides.NotNull=Not annotated method overrides method annotated with @{0} inspection.nullable.problems.parameter.overrides.NotNull=Not annotated parameter overrides @{0} parameter inspection.nullable.problems.primitive.type.annotation=Primitive type members cannot be annotated +inspection.nullable.problems.redundant.annotation.under.container=Redundant nullability annotation in the scope of @{0} inspection.nullable.problems.receiver.annotation=Receiver parameter is inherently not-null inspection.nullable.problems.applied.to.package=Annotation on fully-qualified name must be placed before the last component inspection.nullable.problems.outer.type=Outer type is inherently not-null 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 6ed218f5c493..cb8f45c6de34 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 @@ -210,6 +210,12 @@ public class NullableStuffInspectionBase extends AbstractBaseJavaLocalInspection ? tryCast(modifierList.getParent(), PsiModifierListOwner.class) : null; PsiType targetType = listOwner instanceof PsiMethod method ? method.getReturnType() : listOwner instanceof PsiVariable variable ? variable.getType() : null; + if (listOwner != null && targetType != null) { + checkRedundantInContainerScope(annotation, manager.findContainerAnnotation(listOwner), nullability); + } + else if (type != null) { + checkRedundantInContainerScope(annotation, manager.findDefaultTypeUseNullability(annotation), nullability); + } if (type instanceof PsiPrimitiveType) { LocalQuickFix additionalFix = null; if (targetType instanceof PsiArrayType && targetType.getAnnotations().length == 0) { @@ -281,6 +287,19 @@ public class NullableStuffInspectionBase extends AbstractBaseJavaLocalInspection } } + private void checkRedundantInContainerScope(@NotNull PsiAnnotation annotation, + @Nullable NullabilityAnnotationInfo containerInfo, + @NotNull Nullability nullability) { + if (containerInfo != null && !containerInfo.getAnnotation().equals(annotation) && containerInfo.getNullability() == nullability) { + PsiJavaCodeReferenceElement containerName = containerInfo.getAnnotation().getNameReferenceElement(); + if (containerName != null) { + reportProblem(holder, annotation, + new RemoveAnnotationQuickFix(annotation, null), + "inspection.nullable.problems.redundant.annotation.under.container", containerName.getReferenceName()); + } + } + } + private void checkIllegalLocalAnnotation(@NotNull PsiAnnotation annotation, @Nullable PsiElement owner) { if (owner instanceof PsiLocalVariable || owner instanceof PsiParameter parameter && diff --git a/java/java-tests/testData/inspection/nullableProblems/RedundantNotNull.java b/java/java-tests/testData/inspection/nullableProblems/RedundantNotNull.java new file mode 100644 index 000000000000..1568e4baa281 --- /dev/null +++ b/java/java-tests/testData/inspection/nullableProblems/RedundantNotNull.java @@ -0,0 +1,17 @@ +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.NotNullByDefault; +import java.util.List; + +@NotNullByDefault +class FromDemo { + @NotNull String m(@NotNull String s, @Nullable String s2) { + return ""; + } + + @NotNull String f; + + List<@NotNull String> param(@NotNull String @NotNull [] @NotNull [] a) { + return List.of(); + } +} \ 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 193b3349b17d..e999edb9175c 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInspection/NullableStuffInspectionTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInspection/NullableStuffInspectionTest.java @@ -443,6 +443,11 @@ public class NullableStuffInspectionTest extends LightJavaCodeInsightFixtureTest doTest(); } + public void testRedundantNotNull() { + DataFlowInspectionTestCase.addJetBrainsNotNullByDefault(myFixture); + doTest(); + } + public void testNoNotNullWarningIfIndirectSuperMethodIsAnnotated() { myInspection.REPORT_ANNOTATION_NOT_PROPAGATED_TO_OVERRIDERS = true; myInspection.REPORT_NOTNULL_PARAMETERS_OVERRIDES_NOT_ANNOTATED = true;