From 1115be581a69b36e3c1e76f85180b8913db08bcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Deleuze?= Date: Tue, 21 Jan 2025 16:32:52 +0100 Subject: [PATCH] Add primitive type support to Nullness See gh-34261 --- .../modules/ROOT/pages/core/null-safety.adoc | 5 +++-- .../java/org/springframework/core/Nullness.java | 12 ++++++++---- .../org/springframework/core/NullnessTests.java | 16 ++++++++++++++++ .../testfixture/nullness/JSpecifyProcessor.java | 2 ++ .../testfixture/nullness/NullnessFields.java | 2 ++ 5 files changed, 31 insertions(+), 6 deletions(-) diff --git a/framework-docs/modules/ROOT/pages/core/null-safety.adoc b/framework-docs/modules/ROOT/pages/core/null-safety.adoc index 914e7eadb9a..5264259da13 100644 --- a/framework-docs/modules/ROOT/pages/core/null-safety.adoc +++ b/framework-docs/modules/ROOT/pages/core/null-safety.adoc @@ -13,8 +13,9 @@ annotations such as IntelliJ IDEA or Eclipse) and Kotlin where JSpecify annotati {kotlin-docs}/null-safety.html[Kotlin's null safety]. The {spring-framework-api}/core/Nullness.html[`Nullness` Spring API] can be used at runtime to detect the nullness of a -type usage, a field, a method return type or a parameter. It provides full support for JSpecify annotations and -Kotlin null safety, as well as a pragmatic check on any `@Nullable` annotation (regardless of the package). +type usage, a field, a method return type or a parameter. It provides full support for JSpecify annotations, +Kotlin null safety, Java primitive types, as well as a pragmatic check on any `@Nullable` annotation (regardless of the +package). [[null-safety-libraries]] == Annotating libraries with JSpecify annotations diff --git a/spring-core/src/main/java/org/springframework/core/Nullness.java b/spring-core/src/main/java/org/springframework/core/Nullness.java index e69df973b30..3dd7c1265d5 100644 --- a/spring-core/src/main/java/org/springframework/core/Nullness.java +++ b/spring-core/src/main/java/org/springframework/core/Nullness.java @@ -41,8 +41,9 @@ import org.jspecify.annotations.Nullable; * *

The nullness applies to a type usage, a field, a method return type or a parameter. * JSpecify annotations are fully supported, as well as - * Kotlin null safety and {@code @Nullable} annotations - * regardless of their package (from Spring, JSR-305 or Jakarta set of annotations for example). + * Kotlin null safety, {@code @Nullable} annotations + * regardless of their package (from Spring, JSR-305 or Jakarta set of annotations for example) and Java primitive + * types. * * @author Sebastien Deleuze * @since 7.0 @@ -50,7 +51,7 @@ import org.jspecify.annotations.Nullable; public enum Nullness { /** - * Unspecified nullness (Java and JSpecify {@code @NullUnmarked} defaults). + * Unspecified nullness (Java default for non-primitive types and JSpecify {@code @NullUnmarked} code). */ UNSPECIFIED, @@ -60,7 +61,7 @@ public enum Nullness { NULLABLE, /** - * Will not include null (Kotlin and JSpecify {@code @NullMarked} defaults). + * Will not include null (Kotlin default and JSpecify {@code @NullMarked} code). */ NON_NULL; @@ -130,6 +131,9 @@ public enum Nullness { } private static Nullness jSpecifyNullness(AnnotatedElement annotatedElement, Class declaringClass, AnnotatedType annotatedType) { + if (annotatedType.getType() instanceof Class clazz && clazz.isPrimitive()) { + return (clazz != void.class ? Nullness.NON_NULL : Nullness.UNSPECIFIED); + } if (annotatedType.isAnnotationPresent(Nullable.class)) { return Nullness.NULLABLE; } diff --git a/spring-core/src/test/java/org/springframework/core/NullnessTests.java b/spring-core/src/test/java/org/springframework/core/NullnessTests.java index 2cc0954debf..a9fef1a9894 100644 --- a/spring-core/src/test/java/org/springframework/core/NullnessTests.java +++ b/spring-core/src/test/java/org/springframework/core/NullnessTests.java @@ -377,4 +377,20 @@ public class NullnessTests { Assertions.assertThat(nullness).isEqualTo(Nullness.NULLABLE); } + // Primitive types + + @Test + void primitiveField() throws NoSuchFieldException { + var field = NullnessFields.class.getDeclaredField("primitiveField"); + var nullness = Nullness.forField(field); + Assertions.assertThat(nullness).isEqualTo(Nullness.NON_NULL); + } + + @Test + void voidMethod() throws NoSuchMethodException { + var method = JSpecifyProcessor.class.getMethod("voidProcess"); + var nullness = Nullness.forMethodReturnType(method); + Assertions.assertThat(nullness).isEqualTo(Nullness.UNSPECIFIED); + } + } diff --git a/spring-core/src/testFixtures/java/org/springframework/core/testfixture/nullness/JSpecifyProcessor.java b/spring-core/src/testFixtures/java/org/springframework/core/testfixture/nullness/JSpecifyProcessor.java index a3032f114d1..444d765a3da 100644 --- a/spring-core/src/testFixtures/java/org/springframework/core/testfixture/nullness/JSpecifyProcessor.java +++ b/spring-core/src/testFixtures/java/org/springframework/core/testfixture/nullness/JSpecifyProcessor.java @@ -36,4 +36,6 @@ public interface JSpecifyProcessor { @NullMarked @NonNull String nonNullMarkedProcess(); + + void voidProcess(); } diff --git a/spring-core/src/testFixtures/java/org/springframework/core/testfixture/nullness/NullnessFields.java b/spring-core/src/testFixtures/java/org/springframework/core/testfixture/nullness/NullnessFields.java index 78d71433684..47f1445d1b5 100644 --- a/spring-core/src/testFixtures/java/org/springframework/core/testfixture/nullness/NullnessFields.java +++ b/spring-core/src/testFixtures/java/org/springframework/core/testfixture/nullness/NullnessFields.java @@ -26,4 +26,6 @@ public class NullnessFields { @org.springframework.core.testfixture.nullness.custom.Nullable public String customNullableField; + + public int primitiveField = 0; }