From d2d8283a2aa8d5ed063a8e1eaaa847c3f9232c63 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Mon, 3 Nov 2025 11:25:01 +0100 Subject: [PATCH] [java-inspections] IDEA-381329 IDEA provides the warning for code even if JSpecify added transitively and code in project is not marked. GitOrigin-RevId: 2454b9d89a12c4c5afabb370b84ac17362b56ccc --- .../nullable/NullableStuffInspectionBase.java | 15 +++++++++++++-- .../nullable/NullableStuffInspection.java | 1 + ...atedInstantiationOfNonNullTypeParameter.java | 17 +++++++++++++++++ ...edInstantiationOfNonNullTypeParameterOn.java | 17 +++++++++++++++++ .../NullableStuffInspectionTest.java | 15 +++++++++++++++ .../resources/messages/JavaBundle.properties | 1 + 6 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 java/java-tests/testData/inspection/nullableProblems/UnannotatedInstantiationOfNonNullTypeParameter.java create mode 100644 java/java-tests/testData/inspection/nullableProblems/UnannotatedInstantiationOfNonNullTypeParameterOn.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 e55ee97a3fb1..d5ee37966f53 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 @@ -70,6 +70,7 @@ public class NullableStuffInspectionBase extends AbstractBaseJavaLocalInspection @SuppressWarnings("WeakerAccess") public boolean IGNORE_EXTERNAL_SUPER_NOTNULL; @SuppressWarnings("WeakerAccess") public boolean REPORT_NOTNULL_PARAMETERS_OVERRIDES_NOT_ANNOTATED; @SuppressWarnings("WeakerAccess") public boolean REPORT_NULLABILITY_ANNOTATION_ON_LOCALS = true; + @SuppressWarnings("WeakerAccess") public boolean REPORT_NOT_ANNOTATED_INSTANTIATION_NOT_NULL_TYPE = false; @SuppressWarnings("WeakerAccess") public boolean REPORT_NOT_NULL_TO_NULLABLE_CONFLICTS_IN_ASSIGNMENTS = false; /** * @deprecated the field remains to minimize changes to users' inspection profiles. @@ -100,6 +101,7 @@ public class NullableStuffInspectionBase extends AbstractBaseJavaLocalInspection "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_ANNOTATED_INSTANTIATION_NOT_NULL_TYPE".equals(name) && "false".equals(value) || "REPORT_REDUNDANT_NULLABILITY_ANNOTATION_IN_THE_SCOPE_OF_ANNOTATED_CONTAINER".equals(name) && "true".equals(value)) { node.removeContent(child); } @@ -447,7 +449,8 @@ public class NullableStuffInspectionBase extends AbstractBaseJavaLocalInspection Project project = element.getProject(); PsiType type = typeArgument.getType(); if (TypeNullability.ofTypeParameter(typeParameters[i]).nullability() != Nullability.NOT_NULL) continue; - Nullability typeNullability = DfaPsiUtil.getTypeNullability(type); + TypeNullability nullability = type.getNullability(); + Nullability typeNullability = nullability.nullability(); if (typeNullability != Nullability.NOT_NULL && !(typeNullability == Nullability.UNKNOWN && type instanceof PsiWildcardType wildcardType && !wildcardType.isExtends())) { String annotationToAdd = manager.getDefaultAnnotation(Nullability.NOT_NULL, reference); @@ -458,7 +461,15 @@ public class NullableStuffInspectionBase extends AbstractBaseJavaLocalInspection fixes.add(LocalQuickFix.from(new AddTypeAnnotationFix(typeArgument, annotationToAdd, manager.getNullables()))); } fixes.add(LocalQuickFix.from(createAnnotateAsNullMarkedFix(typeArgument, manager.getNullables()), false)); - reportProblem(holder, typeArgument, fixes.toArray(LocalQuickFix.EMPTY_ARRAY), "non.null.type.argument.is.expected"); + ProblemHighlightType level = + nullability == TypeNullability.UNKNOWN && !REPORT_NOT_ANNOTATED_INSTANTIATION_NOT_NULL_TYPE ? + ProblemHighlightType.INFORMATION : + ProblemHighlightType.GENERIC_ERROR_OR_WARNING; + if (!isOnTheFly && level == ProblemHighlightType.INFORMATION) continue; + holder.registerProblem(typeArgument, + JavaAnalysisBundle.message("non.null.type.argument.is.expected"), + level, + fixes.toArray(LocalQuickFix.EMPTY_ARRAY)); } } } 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 659323926f14..b95e44f8e430 100644 --- a/java/java-impl/src/com/intellij/codeInspection/nullable/NullableStuffInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/nullable/NullableStuffInspection.java @@ -41,6 +41,7 @@ public class NullableStuffInspection extends NullableStuffInspectionBase { checkbox("REPORT_NOTNULL_PARAMETER_OVERRIDES_NULLABLE", JavaBundle.message("inspection.nullable.problems.method.overrides.notnull.option")), checkbox("REPORT_NOT_ANNOTATED_METHOD_OVERRIDES_NOTNULL", JavaBundle.message("inspection.nullable.problems.method.overrides.option"), checkbox("IGNORE_EXTERNAL_SUPER_NOTNULL", JavaBundle.message("inspection.nullable.problems.ignore.external.notnull"))), + checkbox("REPORT_NOT_ANNOTATED_INSTANTIATION_NOT_NULL_TYPE", JavaBundle.message("inspection.nullable.problems.method.non.annotated.type.argument.option")), checkbox("REPORT_NOTNULL_PARAMETERS_OVERRIDES_NOT_ANNOTATED", JavaBundle.message("inspection.nullable.problems.notnull.overrides.option")), checkbox("REPORT_NOT_ANNOTATED_GETTER", JavaBundle.message("inspection.nullable.problems.not.annotated.getters.for.annotated.fields")), checkbox("REPORT_NULLS_PASSED_TO_NOT_NULL_PARAMETER", JavaBundle.message("inspection.nullable.problems.notnull.parameters.with.null.literal.option")), diff --git a/java/java-tests/testData/inspection/nullableProblems/UnannotatedInstantiationOfNonNullTypeParameter.java b/java/java-tests/testData/inspection/nullableProblems/UnannotatedInstantiationOfNonNullTypeParameter.java new file mode 100644 index 000000000000..20e49e7dcc25 --- /dev/null +++ b/java/java-tests/testData/inspection/nullableProblems/UnannotatedInstantiationOfNonNullTypeParameter.java @@ -0,0 +1,17 @@ +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +@NullMarked +class Main { +} + +class Use extends Main { + void test(Main main) { + + } + + void testNullable(Main<@Nullable Integer> nullable) { + + } + +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/nullableProblems/UnannotatedInstantiationOfNonNullTypeParameterOn.java b/java/java-tests/testData/inspection/nullableProblems/UnannotatedInstantiationOfNonNullTypeParameterOn.java new file mode 100644 index 000000000000..7cb1b5a74f93 --- /dev/null +++ b/java/java-tests/testData/inspection/nullableProblems/UnannotatedInstantiationOfNonNullTypeParameterOn.java @@ -0,0 +1,17 @@ +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +@NullMarked +class Main { +} + +class Use extends Main<Double> { + void test(Main<String> main) { + + } + + void testNullable(Main<@Nullable Integer> nullable) { + + } + +} \ 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 d25b761ac285..e61e384efa27 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInspection/NullableStuffInspectionTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInspection/NullableStuffInspectionTest.java @@ -298,10 +298,24 @@ public class NullableStuffInspectionTest extends LightJavaCodeInsightFixtureTest } public void testNotNullTypeArgumentWithNullableSuperType() { + myInspection.REPORT_NOT_ANNOTATED_INSTANTIATION_NOT_NULL_TYPE = true; setupTypeUseAnnotations("typeUse", myFixture); doTest(); } + public void testUnannotatedInstantiationOfNonNullTypeParameter() { + addJSpecifyNullMarked(myFixture); + setupTypeUseAnnotations("org.jspecify.annotations", myFixture); + doTest(); + } + + public void testUnannotatedInstantiationOfNonNullTypeParameterOn() { + myInspection.REPORT_NOT_ANNOTATED_INSTANTIATION_NOT_NULL_TYPE = true; + addJSpecifyNullMarked(myFixture); + setupTypeUseAnnotations("org.jspecify.annotations", myFixture); + doTest(); + } + public void testNullableTypeArgumentSOE() { setupTypeUseAnnotations("typeUse", myFixture); doTest(); @@ -383,6 +397,7 @@ public class NullableStuffInspectionTest extends LightJavaCodeInsightFixtureTest } public void testQuickFixOnTypeArgument() { + myInspection.REPORT_NOT_ANNOTATED_INSTANTIATION_NOT_NULL_TYPE = true; setupTypeUseAnnotations("typeUse", myFixture); doTestWithFix("Annotate as '@NotNull'"); } diff --git a/java/openapi/resources/messages/JavaBundle.properties b/java/openapi/resources/messages/JavaBundle.properties index cfc43da6d34e..95f4a565d173 100644 --- a/java/openapi/resources/messages/JavaBundle.properties +++ b/java/openapi/resources/messages/JavaBundle.properties @@ -557,6 +557,7 @@ inspection.nullable.problems.display.name=@NotNull/@Nullable problems inspection.nullable.problems.ignore.external.notnull=&Ignore external @NotNull inspection.nullable.problems.method.overrides.notnull.option=Report @NotNull ¶meters overriding @Nullable and vice versa inspection.nullable.problems.method.overrides.option=Report non-&annotated parameters or methods overriding @NotNull +inspection.nullable.problems.method.non.annotated.type.argument.option=Report non-&annotated type arguments instantiating non-null type parameter inspection.nullable.problems.not.annotated.getters.for.annotated.fields=Report non-annotated &setter/constructor parameters or getters of annotated fields inspection.nullable.problems.notnull.overrides.option=Report @NotNull ¶meters overriding non-annotated inspection.nullable.problems.notnull.parameters.with.null.literal.option=Report @NotNull parameters with null-literal argument usages