From 7881c922dc3465a59383a4eb356261385f1f3933 Mon Sep 17 00:00:00 2001 From: Marcin Mikosik Date: Wed, 29 Oct 2025 15:44:01 +0000 Subject: [PATCH] [java] IDEA-377335 Add configuration for "Redundant nullability annotation in the scope of annotated container" #IDEA-377335 Fixed Merge-request: IJ-MR-179742 Merged-by: Marcin Mikosik GitOrigin-RevId: 8ea1a93c936c5fe2e84bcee578ac9d21f921535f --- .../messages/JavaAnalysisBundle.properties | 1 + .../nullable/NullableStuffInspectionBase.java | 12 +++++++-- .../nullable/NullableStuffInspection.java | 2 ++ .../RedundantNotNullDisabled.java | 27 +++++++++++++++++++ .../NullableStuffInspectionTest.java | 13 ++++++++- .../resources/messages/JavaBundle.properties | 1 + 6 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 java/java-tests/testData/inspection/nullableProblems/RedundantNotNullDisabled.java diff --git a/java/java-analysis-api/resources/messages/JavaAnalysisBundle.properties b/java/java-analysis-api/resources/messages/JavaAnalysisBundle.properties index ebe4580071e5..3877866036d8 100644 --- a/java/java-analysis-api/resources/messages/JavaAnalysisBundle.properties +++ b/java/java-analysis-api/resources/messages/JavaAnalysisBundle.properties @@ -250,6 +250,7 @@ inspection.nullable.problems.primitive.type.annotation=Primitive type members ca inspection.nullable.problems.nullable.instantiation.of.notnull=Non-null type parameter ''{0}'' cannot be instantiated with @{1} type inspection.nullable.problems.nullable.instantiation.of.notnull.container=Non-null type parameter ''{0}'' cannot be instantiated under @{1} inspection.nullable.problems.redundant.annotation.under.container=Redundant nullability annotation in the scope of @{0} +inspection.nullable.problems.turn.off.redundant.annotation.under.container=Don't report redundant nullability annotation in the scope of annotated container inspection.nullable.problems.redundant.annotation.inherited.notnull=Redundant nullability annotation: type parameter upper bound is already non-null inspection.nullable.problems.receiver.annotation=Receiver parameter is inherently non-null inspection.nullable.problems.applied.to.package=Annotation on fully-qualified name must be placed before the last component 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 4495910e2c28..35d55cc0650f 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 @@ -83,6 +83,7 @@ public class NullableStuffInspectionBase extends AbstractBaseJavaLocalInspection */ @Deprecated @SuppressWarnings("WeakerAccess") public boolean REPORT_NULLS_PASSED_TO_NON_ANNOTATED_METHOD = true; public boolean REPORT_NULLS_PASSED_TO_NOT_NULL_PARAMETER = true; + @SuppressWarnings("WeakerAccess") public boolean REPORT_REDUNDANT_NULLABILITY_ANNOTATION_IN_THE_SCOPE_OF_ANNOTATED_CONTAINER = true; private static final Logger LOG = Logger.getInstance(NullableStuffInspectionBase.class); @@ -97,7 +98,8 @@ public class NullableStuffInspectionBase extends AbstractBaseJavaLocalInspection "REQUIRE_NOTNULL_FIELDS_INITIALIZED".equals(name) && "true".equals(value) || "REPORT_NULLABILITY_ANNOTATION_ON_LOCALS".equals(name) && "true".equals(value) || "REPORT_NULLS_PASSED_TO_NOT_NULL_PARAMETER".equals(name) && "true".equals(value) || - "REPORT_NOT_NULL_TO_NULLABLE_CONFLICTS_IN_ASSIGNMENTS".equals(name) && "false".equals(value)) { + "REPORT_NOT_NULL_TO_NULLABLE_CONFLICTS_IN_ASSIGNMENTS".equals(name) && "false".equals(value) || + "REPORT_REDUNDANT_NULLABILITY_ANNOTATION_IN_THE_SCOPE_OF_ANNOTATED_CONTAINER".equals(name) && "true".equals(value)) { node.removeContent(child); } } @@ -357,11 +359,17 @@ public class NullableStuffInspectionBase extends AbstractBaseJavaLocalInspection private void checkRedundantInContainerScope(@NotNull PsiAnnotation annotation, @Nullable NullabilityAnnotationInfo containerInfo, @NotNull Nullability nullability) { + if (!REPORT_REDUNDANT_NULLABILITY_ANNOTATION_IN_THE_SCOPE_OF_ANNOTATED_CONTAINER) return; if (containerInfo != null && !containerInfo.getAnnotation().equals(annotation) && containerInfo.getNullability() == nullability) { PsiJavaCodeReferenceElement containerName = containerInfo.getAnnotation().getNameReferenceElement(); if (containerName != null) { + LocalQuickFix updateOptionFix = LocalQuickFix.from( + new UpdateInspectionOptionFix(NullableStuffInspectionBase.this, + "REPORT_REDUNDANT_NULLABILITY_ANNOTATION_IN_THE_SCOPE_OF_ANNOTATED_CONTAINER", + JavaAnalysisBundle.message("inspection.nullable.problems.turn.off.redundant.annotation.under.container"), + false)); reportProblem(holder, annotation, - new RemoveAnnotationQuickFix(annotation, null), + LocalQuickFix.notNullElements(new RemoveAnnotationQuickFix(annotation, null), updateOptionFix), "inspection.nullable.problems.redundant.annotation.under.container", containerName.getReferenceName()); } } diff --git a/java/java-impl/src/com/intellij/codeInspection/nullable/NullableStuffInspection.java b/java/java-impl/src/com/intellij/codeInspection/nullable/NullableStuffInspection.java index a72052d74bf1..659323926f14 100644 --- a/java/java-impl/src/com/intellij/codeInspection/nullable/NullableStuffInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/nullable/NullableStuffInspection.java @@ -47,6 +47,8 @@ public class NullableStuffInspection extends NullableStuffInspectionBase { checkbox("REPORT_NULLABILITY_ANNOTATION_ON_LOCALS", JavaBundle.message("inspection.nullable.problems.nullability.on.locals")), checkbox("REPORT_NOT_NULL_TO_NULLABLE_CONFLICTS_IN_ASSIGNMENTS", JavaBundle.message( "inspection.nullable.problems.notnull.to.nullable.assignment.conflicts")), + checkbox("REPORT_REDUNDANT_NULLABILITY_ANNOTATION_IN_THE_SCOPE_OF_ANNOTATED_CONTAINER", JavaBundle.message( + "inspection.nullable.problems.redundant.nullability.inside.container")), JavaInspectionControls.button(JavaInspectionButtons.ButtonKind.NULLABILITY_ANNOTATIONS) ); } diff --git a/java/java-tests/testData/inspection/nullableProblems/RedundantNotNullDisabled.java b/java/java-tests/testData/inspection/nullableProblems/RedundantNotNullDisabled.java new file mode 100644 index 000000000000..6d38c0aa8eaf --- /dev/null +++ b/java/java-tests/testData/inspection/nullableProblems/RedundantNotNullDisabled.java @@ -0,0 +1,27 @@ +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(); + } + + void fun() { + @Nullable String variableWhereAnnotationMakesSense = OtherClass.returnSomething(); + } +} + +class OtherClass { + static String returnSomething() { + return Math.random() > 0.5 ? "not null" : null; + } +} 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 1039f465094f..d25b761ac285 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInspection/NullableStuffInspectionTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInspection/NullableStuffInspectionTest.java @@ -439,7 +439,18 @@ public class NullableStuffInspectionTest extends LightJavaCodeInsightFixtureTest public void testRedundantNotNull() { doTest(); } - + + public void testRedundantNotNullDisabled() { + boolean previousValue = myInspection.REPORT_REDUNDANT_NULLABILITY_ANNOTATION_IN_THE_SCOPE_OF_ANNOTATED_CONTAINER; + try { + myInspection.REPORT_REDUNDANT_NULLABILITY_ANNOTATION_IN_THE_SCOPE_OF_ANNOTATED_CONTAINER = false; + doTest(); + } + finally { + myInspection.REPORT_REDUNDANT_NULLABILITY_ANNOTATION_IN_THE_SCOPE_OF_ANNOTATED_CONTAINER = previousValue; + } + } + public void testRedundantNotNull2() { doTest(); } diff --git a/java/openapi/resources/messages/JavaBundle.properties b/java/openapi/resources/messages/JavaBundle.properties index c3944c260325..afbcfd29353a 100644 --- a/java/openapi/resources/messages/JavaBundle.properties +++ b/java/openapi/resources/messages/JavaBundle.properties @@ -561,6 +561,7 @@ inspection.nullable.problems.notnull.overrides.option=Report @NotNull ¶meter inspection.nullable.problems.notnull.parameters.with.null.literal.option=Report @NotNull parameters with null-literal argument usages inspection.nullable.problems.nullability.on.locals=Report nullability annotations on local variables when they are non-applicable inspection.nullable.problems.notnull.to.nullable.assignment.conflicts=Report assignment of a not null type parameter to nullable type parameter +inspection.nullable.problems.redundant.nullability.inside.container=Report redundant nullability annotation in the scope of annotated container inspection.optional.get.without.is.present.message={0}.#ref() without ''isPresent()'' check inspection.optional.get.without.is.present.method.reference.message=#ref without 'isPresent()' check inspection.overflowing.loop.index.inspection.description=Loop executes zero or billions of times