Introduce Nullness API
This commit introduces a Nullness enum with related utility methods in order to detect if a type usage, a field, a method return type or a parameter is unspecified, nullable or not null. JSpecify annotations are fully supported, as well as Kotlin null safety and `@Nullable` annotations regardless of their package (from Spring, JSR-305 or Jakarta set of annotations for example). Closes gh-34261
This commit is contained in:
parent
92472a6b62
commit
b3e888279e
|
@ -18,7 +18,6 @@ package org.springframework.core;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.reflect.AnnotatedElement;
|
import java.lang.reflect.AnnotatedElement;
|
||||||
import java.lang.reflect.AnnotatedType;
|
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Executable;
|
import java.lang.reflect.Executable;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
@ -388,39 +387,18 @@ public class MethodParameter {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return whether this method indicates a parameter which is not required:
|
* Return whether this method indicates a parameter which is not required:
|
||||||
* either in the form of Java 8's {@link java.util.Optional}, any variant
|
* either in the form of Java 8's {@link java.util.Optional}, JSpecify annotations,
|
||||||
* of a parameter-level {@code Nullable} annotation (such as from JSpecify,
|
* any variant of a parameter-level {@code @Nullable} annotation (such as from Spring,
|
||||||
* JSR-305 or Jakarta set of annotations), or a language-level nullable type
|
* JSR-305 or Jakarta set of annotations), a language-level nullable type
|
||||||
* declaration or {@code Continuation} parameter in Kotlin.
|
* declaration or {@code Continuation} parameter in Kotlin.
|
||||||
* @since 4.3
|
* @since 4.3
|
||||||
|
* @see Nullness#forMethodParameter(MethodParameter)
|
||||||
*/
|
*/
|
||||||
public boolean isOptional() {
|
public boolean isOptional() {
|
||||||
return (getParameterType() == Optional.class || hasNullableAnnotation() ||
|
return (getParameterType() == Optional.class || Nullness.forMethodParameter(this) == Nullness.NULLABLE ||
|
||||||
(KotlinDetector.isKotlinType(getContainingClass()) && KotlinDelegate.isOptional(this)));
|
(KotlinDetector.isKotlinType(getContainingClass()) && KotlinDelegate.isOptional(this)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check whether this method parameter is annotated with any variant of a
|
|
||||||
* {@code Nullable} annotation, for example, {@code org.springframework.lang.Nullable},
|
|
||||||
* {@code org.jspecify.annotations.Nullable} or {@code jakarta.annotation.Nullable}.
|
|
||||||
*/
|
|
||||||
private boolean hasNullableAnnotation() {
|
|
||||||
for (Annotation ann : getParameterAnnotations()) {
|
|
||||||
if ("Nullable".equals(ann.annotationType().getSimpleName())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (this.parameterIndex >= 0) {
|
|
||||||
AnnotatedType annotatedType = this.executable.getAnnotatedParameterTypes()[this.parameterIndex];
|
|
||||||
for (Annotation ann : annotatedType.getAnnotations()) {
|
|
||||||
if ("Nullable".equals(ann.annotationType().getSimpleName())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a variant of this {@code MethodParameter} which points to
|
* Return a variant of this {@code MethodParameter} which points to
|
||||||
* the same parameter but one nesting level deeper in case of a
|
* the same parameter but one nesting level deeper in case of a
|
||||||
|
|
|
@ -0,0 +1,210 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2025 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.core;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.AnnotatedElement;
|
||||||
|
import java.lang.reflect.AnnotatedType;
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.Executable;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Parameter;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
import kotlin.reflect.KFunction;
|
||||||
|
import kotlin.reflect.KParameter;
|
||||||
|
import kotlin.reflect.KProperty;
|
||||||
|
import kotlin.reflect.jvm.ReflectJvmMapping;
|
||||||
|
import org.jspecify.annotations.NonNull;
|
||||||
|
import org.jspecify.annotations.NullMarked;
|
||||||
|
import org.jspecify.annotations.NullUnmarked;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constants that indicate the nullness, as well as related utility methods.
|
||||||
|
*
|
||||||
|
* <p>The nullness applies to a type usage, a field, a method return type or a parameter.
|
||||||
|
* <a href="https://jspecify.dev/docs/user-guide/">JSpecify annotations</a> are fully supported, as well as
|
||||||
|
* <a href="https://kotlinlang.org/docs/null-safety.html">Kotlin null safety</a> and {@code @Nullable} annotations
|
||||||
|
* regardless of their package (from Spring, JSR-305 or Jakarta set of annotations for example).
|
||||||
|
*
|
||||||
|
* @author Sebastien Deleuze
|
||||||
|
* @since 7.0
|
||||||
|
*/
|
||||||
|
public enum Nullness {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unspecified nullness (Java and JSpecify {@code @NullUnmarked} defaults).
|
||||||
|
*/
|
||||||
|
UNSPECIFIED,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can include null (typically specified with a {@code @Nullable} annotation).
|
||||||
|
*/
|
||||||
|
NULLABLE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will not include null (Kotlin and JSpecify {@code @NullMarked} defaults).
|
||||||
|
*/
|
||||||
|
NON_NULL;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the nullness of the given method return type.
|
||||||
|
* @param method the source for the method return type
|
||||||
|
* @return the corresponding nullness
|
||||||
|
*/
|
||||||
|
public static Nullness forMethodReturnType(Method method) {
|
||||||
|
if (KotlinDetector.isKotlinType(method.getDeclaringClass())) {
|
||||||
|
return KotlinDelegate.forMethodReturnType(method);
|
||||||
|
}
|
||||||
|
return (hasNullableAnnotation(method) ? Nullness.NULLABLE :
|
||||||
|
jSpecifyNullness(method, method.getDeclaringClass(), method.getAnnotatedReturnType()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the nullness of the given parameter.
|
||||||
|
* @param parameter the parameter descriptor
|
||||||
|
* @return the corresponding nullness
|
||||||
|
*/
|
||||||
|
public static Nullness forParameter(Parameter parameter) {
|
||||||
|
if (KotlinDetector.isKotlinType(parameter.getDeclaringExecutable().getDeclaringClass())) {
|
||||||
|
// TODO Optimize when kotlin-reflect provide a more direct Parameter to KParameter resolution
|
||||||
|
MethodParameter methodParameter = MethodParameter.forParameter(parameter);
|
||||||
|
return KotlinDelegate.forParameter(methodParameter.getExecutable(), methodParameter.getParameterIndex());
|
||||||
|
}
|
||||||
|
Executable executable = parameter.getDeclaringExecutable();
|
||||||
|
return (hasNullableAnnotation(parameter) ? Nullness.NULLABLE :
|
||||||
|
jSpecifyNullness(executable, executable.getDeclaringClass(), parameter.getAnnotatedType()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the nullness of the given method parameter.
|
||||||
|
* @param methodParameter the method parameter descriptor
|
||||||
|
* @return the corresponding nullness
|
||||||
|
*/
|
||||||
|
public static Nullness forMethodParameter(MethodParameter methodParameter) {
|
||||||
|
return (methodParameter.getParameterIndex() < 0 ?
|
||||||
|
forMethodReturnType(Objects.requireNonNull(methodParameter.getMethod())) :
|
||||||
|
forParameter(methodParameter.getParameter()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the nullness of the given field.
|
||||||
|
* @param field the field descriptor
|
||||||
|
* @return the corresponding nullness
|
||||||
|
*/
|
||||||
|
public static Nullness forField(Field field) {
|
||||||
|
if (KotlinDetector.isKotlinType(field.getDeclaringClass())) {
|
||||||
|
return KotlinDelegate.forField(field);
|
||||||
|
}
|
||||||
|
return (hasNullableAnnotation(field) ? Nullness.NULLABLE :
|
||||||
|
jSpecifyNullness(field, field.getDeclaringClass(), field.getAnnotatedType()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Check method and parameter level @Nullable annotations regardless of the package (including Spring and JSR 305 annotations)
|
||||||
|
private static boolean hasNullableAnnotation(AnnotatedElement element) {
|
||||||
|
for (Annotation annotation : element.getDeclaredAnnotations()) {
|
||||||
|
if ("Nullable".equals(annotation.annotationType().getSimpleName())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Nullness jSpecifyNullness(AnnotatedElement annotatedElement, Class<?> declaringClass, AnnotatedType annotatedType) {
|
||||||
|
if (annotatedType.isAnnotationPresent(Nullable.class)) {
|
||||||
|
return Nullness.NULLABLE;
|
||||||
|
}
|
||||||
|
if (annotatedType.isAnnotationPresent(NonNull.class)) {
|
||||||
|
return Nullness.NON_NULL;
|
||||||
|
}
|
||||||
|
Nullness nullness = Nullness.UNSPECIFIED;
|
||||||
|
// Package level
|
||||||
|
Package declaringPackage = declaringClass.getPackage();
|
||||||
|
if (declaringPackage.isAnnotationPresent(NullMarked.class)) {
|
||||||
|
nullness = Nullness.NON_NULL;
|
||||||
|
}
|
||||||
|
// Class level
|
||||||
|
if (declaringClass.isAnnotationPresent(NullMarked.class)) {
|
||||||
|
nullness = Nullness.NON_NULL;
|
||||||
|
}
|
||||||
|
else if (declaringClass.isAnnotationPresent(NullUnmarked.class)) {
|
||||||
|
nullness = Nullness.UNSPECIFIED;
|
||||||
|
}
|
||||||
|
// Annotated element level
|
||||||
|
if (annotatedElement.isAnnotationPresent(NullMarked.class)) {
|
||||||
|
nullness = Nullness.NON_NULL;
|
||||||
|
}
|
||||||
|
else if (annotatedElement.isAnnotationPresent(NullUnmarked.class)) {
|
||||||
|
nullness = Nullness.UNSPECIFIED;
|
||||||
|
}
|
||||||
|
return nullness;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inner class to avoid a hard dependency on Kotlin at runtime.
|
||||||
|
*/
|
||||||
|
private static class KotlinDelegate {
|
||||||
|
|
||||||
|
|
||||||
|
public static Nullness forMethodReturnType(Method method) {
|
||||||
|
KFunction<?> function = ReflectJvmMapping.getKotlinFunction(method);
|
||||||
|
if (function != null && function.getReturnType().isMarkedNullable()) {
|
||||||
|
return Nullness.NULLABLE;
|
||||||
|
}
|
||||||
|
return Nullness.NON_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Nullness forParameter(Executable executable, int parameterIndex) {
|
||||||
|
KFunction<?> function;
|
||||||
|
Predicate<KParameter> predicate;
|
||||||
|
if (executable instanceof Method method) {
|
||||||
|
function = ReflectJvmMapping.getKotlinFunction(method);
|
||||||
|
predicate = p -> KParameter.Kind.VALUE.equals(p.getKind());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
function = ReflectJvmMapping.getKotlinFunction((Constructor<?>) executable);
|
||||||
|
predicate = p -> (KParameter.Kind.VALUE.equals(p.getKind()) ||
|
||||||
|
KParameter.Kind.INSTANCE.equals(p.getKind()));
|
||||||
|
}
|
||||||
|
if (function == null) {
|
||||||
|
return Nullness.UNSPECIFIED;
|
||||||
|
}
|
||||||
|
int i = 0;
|
||||||
|
for (KParameter kParameter : function.getParameters()) {
|
||||||
|
if (predicate.test(kParameter) && parameterIndex == i++) {
|
||||||
|
return (kParameter.getType().isMarkedNullable() ? Nullness.NULLABLE : Nullness.NON_NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Nullness.UNSPECIFIED;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Nullness forField(Field field) {
|
||||||
|
KProperty<?> property = ReflectJvmMapping.getKotlinProperty(field);
|
||||||
|
if (property != null && property.getReturnType().isMarkedNullable()) {
|
||||||
|
return Nullness.NULLABLE;
|
||||||
|
}
|
||||||
|
return Nullness.NON_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,380 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2025 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.core;
|
||||||
|
|
||||||
|
import org.assertj.core.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.core.testfixture.nullness.ClassMarkedJSpecifyProcessor;
|
||||||
|
import org.springframework.core.testfixture.nullness.CustomNullableProcessor;
|
||||||
|
import org.springframework.core.testfixture.nullness.JSpecifyProcessor;
|
||||||
|
import org.springframework.core.testfixture.nullness.NullnessFields;
|
||||||
|
import org.springframework.core.testfixture.nullness.marked.PackageMarkedJSpecifyProcessor;
|
||||||
|
import org.springframework.core.testfixture.nullness.marked.unmarked.PackageUnmarkedJSpecifyProcessor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link Nullness}.
|
||||||
|
*
|
||||||
|
* @author Sebastien Deleuze
|
||||||
|
*/
|
||||||
|
public class NullnessTests {
|
||||||
|
|
||||||
|
// JSpecify without @NullMarked and @NullUnmarked
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jspecifyUnspecifiedReturnType() throws NoSuchMethodException {
|
||||||
|
var method = JSpecifyProcessor.class.getMethod("process", String.class, String.class, String.class);
|
||||||
|
var nullness = Nullness.forMethodReturnType(method);
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.UNSPECIFIED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jspecifyNullableReturnType() throws NoSuchMethodException {
|
||||||
|
var method = JSpecifyProcessor.class.getMethod("nullableProcess");
|
||||||
|
var nullness = Nullness.forMethodReturnType(method);
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.NULLABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jspecifyNonNullReturnType() throws NoSuchMethodException {
|
||||||
|
var method = JSpecifyProcessor.class.getMethod("nonNullProcess");
|
||||||
|
var nullness = Nullness.forMethodReturnType(method);
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.NON_NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jspecifyUnspecifiedParameter() throws NoSuchMethodException {
|
||||||
|
var method = JSpecifyProcessor.class.getMethod("process", String.class, String.class, String.class);
|
||||||
|
var nullness = Nullness.forParameter(method.getParameters()[0]);
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.UNSPECIFIED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jspecifyNullableParameter() throws NoSuchMethodException {
|
||||||
|
var method = JSpecifyProcessor.class.getMethod("process", String.class, String.class, String.class);
|
||||||
|
var nullness = Nullness.forParameter(method.getParameters()[1]);
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.NULLABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jspecifyNonNullParameter() throws NoSuchMethodException {
|
||||||
|
var method = JSpecifyProcessor.class.getMethod("process", String.class, String.class, String.class);
|
||||||
|
var nullness = Nullness.forParameter(method.getParameters()[2]);
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.NON_NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// JSpecify with MethodParameter without @NullMarked and @NullUnmarked
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jspecifyUnspecifiedReturnTypeWithMethodParameter() throws NoSuchMethodException {
|
||||||
|
var method = JSpecifyProcessor.class.getMethod("process", String.class, String.class, String.class);
|
||||||
|
var methodParameter = MethodParameter.forExecutable(method, -1);
|
||||||
|
var nullness = Nullness.forMethodParameter(methodParameter);
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.UNSPECIFIED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jspecifyNullableReturnTypeWithMethodParameter() throws NoSuchMethodException {
|
||||||
|
var method = JSpecifyProcessor.class.getMethod("nullableProcess");
|
||||||
|
var methodParameter = MethodParameter.forExecutable(method, -1);
|
||||||
|
var nullness = Nullness.forMethodParameter(methodParameter);
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.NULLABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jspecifyNonNullReturnTypeWithMethodParameter() throws NoSuchMethodException {
|
||||||
|
var method = JSpecifyProcessor.class.getMethod("nonNullProcess");
|
||||||
|
var methodParameter = MethodParameter.forExecutable(method, -1);
|
||||||
|
var nullness = Nullness.forMethodParameter(methodParameter);
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.NON_NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jspecifyUnspecifiedParameterWithMethodParameter() throws NoSuchMethodException {
|
||||||
|
var method = JSpecifyProcessor.class.getMethod("process", String.class, String.class, String.class);
|
||||||
|
var methodParameter = MethodParameter.forExecutable(method, 0);
|
||||||
|
var nullness = Nullness.forMethodParameter(methodParameter);
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.UNSPECIFIED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jspecifyNullableParameterWithMethodParameter() throws NoSuchMethodException {
|
||||||
|
var method = JSpecifyProcessor.class.getMethod("process", String.class, String.class, String.class);
|
||||||
|
var methodParameter = MethodParameter.forExecutable(method, 1);
|
||||||
|
var nullness = Nullness.forMethodParameter(methodParameter);
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.NULLABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jspecifyNonNullParameterWithMethodParameter() throws NoSuchMethodException {
|
||||||
|
var method = JSpecifyProcessor.class.getMethod("process", String.class, String.class, String.class);
|
||||||
|
var methodParameter = MethodParameter.forExecutable(method, 2);
|
||||||
|
var nullness = Nullness.forMethodParameter(methodParameter);
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.NON_NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// JSpecify with Field without @NullMarked and @NullUnmarked
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jspecifyUnspecifiedWithField() throws NoSuchFieldException {
|
||||||
|
var field = NullnessFields.class.getDeclaredField("unannotatedField");
|
||||||
|
var nullness = Nullness.forField(field);
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.UNSPECIFIED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jspecifyNullableWithField() throws NoSuchFieldException {
|
||||||
|
var field = NullnessFields.class.getDeclaredField("jspecifyNullableField");
|
||||||
|
var nullness = Nullness.forField(field);
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.NULLABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jspecifyNonNullWithField() throws NoSuchFieldException {
|
||||||
|
var field = NullnessFields.class.getDeclaredField("jspecifyNonNullField");
|
||||||
|
var nullness = Nullness.forField(field);
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.NON_NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// JSpecify with method-level @NullMarked
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jspecifyMethodMarkedUnspecifiedReturnType() throws NoSuchMethodException {
|
||||||
|
var method = JSpecifyProcessor.class.getMethod("markedProcess", String.class, String.class, String.class);
|
||||||
|
var nullness = Nullness.forMethodReturnType(method);
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.NON_NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jspecifyMethodMarkedNullableReturnType() throws NoSuchMethodException {
|
||||||
|
var method = JSpecifyProcessor.class.getMethod("nullableMarkedProcess");
|
||||||
|
var nullness = Nullness.forMethodReturnType(method);
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.NULLABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jspecifyMethodMarkedNonNullReturnType() throws NoSuchMethodException {
|
||||||
|
var method = JSpecifyProcessor.class.getMethod("nonNullMarkedProcess");
|
||||||
|
var nullness = Nullness.forMethodReturnType(method);
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.NON_NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jspecifyMethodMarkedUnspecifiedParameter() throws NoSuchMethodException {
|
||||||
|
var method = JSpecifyProcessor.class.getMethod("markedProcess", String.class, String.class, String.class);
|
||||||
|
var nullness = Nullness.forParameter(method.getParameters()[0]);
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.NON_NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jspecifyMethodMarkedNullableParameter() throws NoSuchMethodException {
|
||||||
|
var method = JSpecifyProcessor.class.getMethod("markedProcess", String.class, String.class, String.class);
|
||||||
|
var nullness = Nullness.forParameter(method.getParameters()[1]);
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.NULLABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jspecifyMethodMarkedNonNullParameter() throws NoSuchMethodException {
|
||||||
|
var method = JSpecifyProcessor.class.getMethod("markedProcess", String.class, String.class, String.class);
|
||||||
|
var nullness = Nullness.forParameter(method.getParameters()[2]);
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.NON_NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// JSpecify with class-level @NullMarked
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jspecifyClassMarkedUnspecifiedReturnType() throws NoSuchMethodException {
|
||||||
|
var method = ClassMarkedJSpecifyProcessor.class.getMethod("process", String.class, String.class, String.class);
|
||||||
|
var nullness = Nullness.forMethodReturnType(method);
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.NON_NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jspecifyClassMarkedNullableReturnType() throws NoSuchMethodException {
|
||||||
|
var method = ClassMarkedJSpecifyProcessor.class.getMethod("nullableProcess");
|
||||||
|
var nullness = Nullness.forMethodReturnType(method);
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.NULLABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jspecifyClassMarkedNonNullReturnType() throws NoSuchMethodException {
|
||||||
|
var method = ClassMarkedJSpecifyProcessor.class.getMethod("nonNullProcess");
|
||||||
|
var nullness = Nullness.forMethodReturnType(method);
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.NON_NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jspecifyClassMarkedUnspecifiedParameter() throws NoSuchMethodException {
|
||||||
|
var method = ClassMarkedJSpecifyProcessor.class.getMethod("process", String.class, String.class, String.class);
|
||||||
|
var nullness = Nullness.forParameter(method.getParameters()[0]);
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.NON_NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jspecifyClassMarkedNullableParameter() throws NoSuchMethodException {
|
||||||
|
var method = ClassMarkedJSpecifyProcessor.class.getMethod("process", String.class, String.class, String.class);
|
||||||
|
var nullness = Nullness.forParameter(method.getParameters()[1]);
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.NULLABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jspecifyClassMarkedNonNullParameter() throws NoSuchMethodException {
|
||||||
|
var method = ClassMarkedJSpecifyProcessor.class.getMethod("process", String.class, String.class, String.class);
|
||||||
|
var nullness = Nullness.forParameter(method.getParameters()[2]);
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.NON_NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jspecifyClassMarkedMethodUnmarkedUnspecifiedReturnType() throws NoSuchMethodException {
|
||||||
|
var method = ClassMarkedJSpecifyProcessor.class.getMethod("unmarkedProcess", String.class, String.class, String.class);
|
||||||
|
var nullness = Nullness.forMethodReturnType(method);
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.UNSPECIFIED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jspecifyClassMarkedMethodUnmarkedUnspecifiedParameter() throws NoSuchMethodException {
|
||||||
|
var method = ClassMarkedJSpecifyProcessor.class.getMethod("unmarkedProcess", String.class, String.class, String.class);
|
||||||
|
var nullness = Nullness.forParameter(method.getParameters()[0]);
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.UNSPECIFIED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jspecifyClassMarkedMethodUnmarkedNullableParameter() throws NoSuchMethodException {
|
||||||
|
var method = ClassMarkedJSpecifyProcessor.class.getMethod("unmarkedProcess", String.class, String.class, String.class);
|
||||||
|
var nullness = Nullness.forParameter(method.getParameters()[1]);
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.NULLABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jspecifyClassMarkedMethodUnmarkedNonNullParameter() throws NoSuchMethodException {
|
||||||
|
var method = ClassMarkedJSpecifyProcessor.class.getMethod("unmarkedProcess", String.class, String.class, String.class);
|
||||||
|
var nullness = Nullness.forParameter(method.getParameters()[2]);
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.NON_NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// JSpecify with package-level @NullMarked
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jspecifyPackageMarkedUnspecifiedReturnType() throws NoSuchMethodException {
|
||||||
|
var method = PackageMarkedJSpecifyProcessor.class.getMethod("process", String.class, String.class, String.class);
|
||||||
|
var nullness = Nullness.forMethodReturnType(method);
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.NON_NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jspecifyPackageMarkedNullableReturnType() throws NoSuchMethodException {
|
||||||
|
var method = PackageMarkedJSpecifyProcessor.class.getMethod("nullableProcess");
|
||||||
|
var nullness = Nullness.forMethodReturnType(method);
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.NULLABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jspecifyPackageMarkedNonNullReturnType() throws NoSuchMethodException {
|
||||||
|
var method = PackageMarkedJSpecifyProcessor.class.getMethod("nonNullProcess");
|
||||||
|
var nullness = Nullness.forMethodReturnType(method);
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.NON_NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jspecifyPackageMarkedUnspecifiedParameter() throws NoSuchMethodException {
|
||||||
|
var method = PackageMarkedJSpecifyProcessor.class.getMethod("process", String.class, String.class, String.class);
|
||||||
|
var nullness = Nullness.forParameter(method.getParameters()[0]);
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.NON_NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jspecifyPackageMarkedNullableParameter() throws NoSuchMethodException {
|
||||||
|
var method = PackageMarkedJSpecifyProcessor.class.getMethod("process", String.class, String.class, String.class);
|
||||||
|
var nullness = Nullness.forParameter(method.getParameters()[1]);
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.NULLABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jspecifyPackageMarkedNonNullParameter() throws NoSuchMethodException {
|
||||||
|
var method = PackageMarkedJSpecifyProcessor.class.getMethod("process", String.class, String.class, String.class);
|
||||||
|
var nullness = Nullness.forParameter(method.getParameters()[2]);
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.NON_NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// JSpecify with package-level @NullUnmarked
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jspecifyPackageUnmarkedUnspecifiedReturnType() throws NoSuchMethodException {
|
||||||
|
var method = PackageUnmarkedJSpecifyProcessor.class.getMethod("process", String.class, String.class, String.class);
|
||||||
|
var nullness = Nullness.forMethodReturnType(method);
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.UNSPECIFIED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jspecifyPackageUnmarkedNullableReturnType() throws NoSuchMethodException {
|
||||||
|
var method = PackageUnmarkedJSpecifyProcessor.class.getMethod("nullableProcess");
|
||||||
|
var nullness = Nullness.forMethodReturnType(method);
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.NULLABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jspecifyPackageUnmarkedNonNullReturnType() throws NoSuchMethodException {
|
||||||
|
var method = PackageUnmarkedJSpecifyProcessor.class.getMethod("nonNullProcess");
|
||||||
|
var nullness = Nullness.forMethodReturnType(method);
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.NON_NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jspecifyPackageUnmarkedUnspecifiedParameter() throws NoSuchMethodException {
|
||||||
|
var method = PackageUnmarkedJSpecifyProcessor.class.getMethod("process", String.class, String.class, String.class);
|
||||||
|
var nullness = Nullness.forParameter(method.getParameters()[0]);
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.UNSPECIFIED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jspecifyPackageUnmarkedNullableParameter() throws NoSuchMethodException {
|
||||||
|
var method = PackageUnmarkedJSpecifyProcessor.class.getMethod("process", String.class, String.class, String.class);
|
||||||
|
var nullness = Nullness.forParameter(method.getParameters()[1]);
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.NULLABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jspecifyPackageUnmarkedNonNullParameter() throws NoSuchMethodException {
|
||||||
|
var method = PackageUnmarkedJSpecifyProcessor.class.getMethod("process", String.class, String.class, String.class);
|
||||||
|
var nullness = Nullness.forParameter(method.getParameters()[2]);
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.NON_NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Custom @Nullable
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void customNullableReturnType() throws NoSuchMethodException {
|
||||||
|
var method = CustomNullableProcessor.class.getMethod("process", String.class);
|
||||||
|
var nullness = Nullness.forMethodReturnType(method);
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.NULLABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void customNullableParameter() throws NoSuchMethodException {
|
||||||
|
var method = CustomNullableProcessor.class.getMethod("process", String.class);
|
||||||
|
var nullness = Nullness.forParameter(method.getParameters()[0]);
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.NULLABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void customNullableField() throws NoSuchFieldException {
|
||||||
|
var field = NullnessFields.class.getDeclaredField("customNullableField");
|
||||||
|
var nullness = Nullness.forField(field);
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.NULLABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2025 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.core
|
||||||
|
|
||||||
|
import org.assertj.core.api.Assertions
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import kotlin.reflect.jvm.javaMethod
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kotlin tests for [Nullness].
|
||||||
|
*
|
||||||
|
* @author Sebastien Deleuze
|
||||||
|
*/
|
||||||
|
class NullnessKotlinTests {
|
||||||
|
|
||||||
|
val nullableProperty: String? = ""
|
||||||
|
val nonNullProperty: String = ""
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun nullableReturnType() {
|
||||||
|
val method = ::nullable.javaMethod!!
|
||||||
|
val nullness = Nullness.forMethodReturnType(method)
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.NULLABLE)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun nullableParameter() {
|
||||||
|
val method = ::nullable.javaMethod!!
|
||||||
|
val nullness = Nullness.forParameter(method.parameters[0])
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.NULLABLE)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun nonNullReturnType() {
|
||||||
|
val method = ::nonNull.javaMethod!!
|
||||||
|
val nullness = Nullness.forMethodReturnType(method)
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.NON_NULL)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun nonNullParameter() {
|
||||||
|
val method = ::nonNull.javaMethod!!
|
||||||
|
val nullness = Nullness.forParameter(method.parameters[0])
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.NON_NULL)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun nullableProperty() {
|
||||||
|
val field = javaClass.getDeclaredField("nullableProperty")
|
||||||
|
val nullness = Nullness.forField(field)
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.NULLABLE)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun nonNullProperty() {
|
||||||
|
val field = javaClass.getDeclaredField("nonNullProperty")
|
||||||
|
val nullness = Nullness.forField(field)
|
||||||
|
Assertions.assertThat(nullness).isEqualTo(Nullness.NON_NULL)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("unused_parameter")
|
||||||
|
fun nullable(nullable: String?): String? = "foo"
|
||||||
|
|
||||||
|
@Suppress("unused_parameter")
|
||||||
|
fun nonNull(nonNull: String): String = "foo"
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2025 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.core.testfixture.nullness;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.NonNull;
|
||||||
|
import org.jspecify.annotations.NullMarked;
|
||||||
|
import org.jspecify.annotations.NullUnmarked;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
|
@NullMarked
|
||||||
|
public interface ClassMarkedJSpecifyProcessor {
|
||||||
|
|
||||||
|
String process(String unspecified, @Nullable String nullable, @NonNull String nonNull);
|
||||||
|
|
||||||
|
@Nullable String nullableProcess();
|
||||||
|
|
||||||
|
@NonNull String nonNullProcess();
|
||||||
|
|
||||||
|
@NullUnmarked
|
||||||
|
String unmarkedProcess(String unspecified, @Nullable String nullable, @NonNull String nonNull);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2025 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.core.testfixture.nullness;
|
||||||
|
|
||||||
|
public interface CustomNullableProcessor {
|
||||||
|
|
||||||
|
@org.springframework.core.testfixture.nullness.custom.Nullable
|
||||||
|
String process(@org.springframework.core.testfixture.nullness.custom.Nullable String nullable);
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2025 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.core.testfixture.nullness;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.NonNull;
|
||||||
|
import org.jspecify.annotations.NullMarked;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
|
public interface JSpecifyProcessor {
|
||||||
|
|
||||||
|
String process(String unspecified, @Nullable String nullable, @NonNull String nonNull);
|
||||||
|
|
||||||
|
@Nullable String nullableProcess();
|
||||||
|
|
||||||
|
@NonNull String nonNullProcess();
|
||||||
|
|
||||||
|
@NullMarked
|
||||||
|
String markedProcess(String unspecified, @Nullable String nullable, @NonNull String nonNull);
|
||||||
|
|
||||||
|
@NullMarked
|
||||||
|
@Nullable String nullableMarkedProcess();
|
||||||
|
|
||||||
|
@NullMarked
|
||||||
|
@NonNull String nonNullMarkedProcess();
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2025 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.core.testfixture.nullness;
|
||||||
|
|
||||||
|
public class NullnessFields {
|
||||||
|
|
||||||
|
public String unannotatedField = "";
|
||||||
|
|
||||||
|
public @org.jspecify.annotations.Nullable String jspecifyNullableField;
|
||||||
|
|
||||||
|
public @org.jspecify.annotations.NonNull String jspecifyNonNullField = "";
|
||||||
|
|
||||||
|
@org.springframework.core.testfixture.nullness.custom.Nullable
|
||||||
|
public String customNullableField;
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2025 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.core.testfixture.nullness.custom;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface Nullable {
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2025 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.core.testfixture.nullness.marked;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.NonNull;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
|
public interface PackageMarkedJSpecifyProcessor {
|
||||||
|
|
||||||
|
String process(String unspecified, @Nullable String nullable, @NonNull String nonNull);
|
||||||
|
|
||||||
|
@Nullable String nullableProcess();
|
||||||
|
|
||||||
|
@NonNull String nonNullProcess();
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
@NullMarked
|
||||||
|
package org.springframework.core.testfixture.nullness.marked;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.NullMarked;
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2025 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.core.testfixture.nullness.marked.unmarked;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.NonNull;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
|
public interface PackageUnmarkedJSpecifyProcessor {
|
||||||
|
|
||||||
|
String process(String unspecified, @Nullable String nullable, @NonNull String nonNull);
|
||||||
|
|
||||||
|
@Nullable String nullableProcess();
|
||||||
|
|
||||||
|
@NonNull String nonNullProcess();
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
@NullUnmarked
|
||||||
|
package org.springframework.core.testfixture.nullness.marked.unmarked;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.NullUnmarked;
|
Loading…
Reference in New Issue