Fix nullability issues for primitives and types in default package.
See: #3242
This commit is contained in:
@@ -33,6 +33,7 @@ import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.annotation.MergedAnnotations;
|
||||
import org.springframework.lang.Contract;
|
||||
import org.springframework.lang.NonNullApi;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
@@ -100,10 +101,21 @@ public abstract class NullableUtils {
|
||||
*
|
||||
* @param method the method to inspect.
|
||||
* @param elementType the element type.
|
||||
* @return {@literal true} if {@link ElementType} allows {@literal null} values by default.
|
||||
* @return {@literal false} if {@link ElementType} allows {@literal null} values by default. {@literal true} if the
|
||||
* methods return type is a {@link Class#isPrimitive() primitive} and {@literal false} if the method is
|
||||
* {@literal void}.
|
||||
* @see #isNonNull(Annotation, ElementType)
|
||||
*/
|
||||
public static boolean isNonNull(Method method, ElementType elementType) {
|
||||
|
||||
Class<?> returnType = method.getReturnType();
|
||||
|
||||
if (ReflectionUtils.isVoid(returnType)) {
|
||||
return false;
|
||||
} else if (returnType.isPrimitive()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return isNonNull(method.getDeclaringClass(), elementType) || isNonNull((AnnotatedElement) method, elementType);
|
||||
}
|
||||
|
||||
@@ -114,10 +126,16 @@ public abstract class NullableUtils {
|
||||
*
|
||||
* @param type the class to inspect.
|
||||
* @param elementType the element type.
|
||||
* @return {@literal true} if {@link ElementType} allows {@literal null} values by default.
|
||||
* @return {@literal false} if {@link ElementType} allows {@literal null} values. {@literal true} the given
|
||||
* {@literal type} is a {@link Class#isPrimitive() primitive}.
|
||||
* @see #isNonNull(Annotation, ElementType)
|
||||
*/
|
||||
public static boolean isNonNull(Class<?> type, ElementType elementType) {
|
||||
|
||||
if (type.isPrimitive()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return isNonNull(type.getPackage(), elementType) || isNonNull((AnnotatedElement) type, elementType);
|
||||
}
|
||||
|
||||
@@ -126,11 +144,17 @@ public abstract class NullableUtils {
|
||||
* This method determines default {@code javax.annotation.Nonnull nullability} rules from the annotated element
|
||||
*
|
||||
* @param element the scope of declaration, may be a {@link Package}, {@link Class}, or
|
||||
* {@link java.lang.reflect.Method}.
|
||||
* {@link java.lang.reflect.Method}. Can be {@literal null}.
|
||||
* @param elementType the element type.
|
||||
* @return {@literal true} if {@link ElementType} allows {@literal null} values by default.
|
||||
* @return {@literal false} if {@link ElementType} allows {@literal null} values by default or if the given
|
||||
* {@link AnnotatedElement} is {@literal null}.
|
||||
*/
|
||||
public static boolean isNonNull(AnnotatedElement element, ElementType elementType) {
|
||||
@Contract("null, _ -> false")
|
||||
public static boolean isNonNull(@Nullable AnnotatedElement element, ElementType elementType) {
|
||||
|
||||
if (element == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (Annotation annotation : element.getAnnotations()) {
|
||||
|
||||
@@ -157,8 +181,7 @@ public abstract class NullableUtils {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!MergedAnnotations.from(annotation.annotationType()).isPresent(annotationClass)
|
||||
|| !isNonNull(annotation)) {
|
||||
if (!MergedAnnotations.from(annotation.annotationType()).isPresent(annotationClass) || !isNonNull(annotation)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ import org.springframework.util.ReflectionUtils;
|
||||
* Unit tests for {@link NullableUtils}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class NullableUtilsUnitTests {
|
||||
|
||||
@@ -115,4 +116,29 @@ class NullableUtilsUnitTests {
|
||||
|
||||
assertThat(NullableUtils.isExplicitNullable(new MethodParameter(method, -1))).isTrue();
|
||||
}
|
||||
|
||||
@Test // GH-3242
|
||||
void shouldConsiderPrimitivesNonNullable() {
|
||||
|
||||
var method = ReflectionUtils.findMethod(NullableAnnotatedType.class, "primitiveReturn");
|
||||
|
||||
assertThat(NullableUtils.isNonNull(method, ElementType.METHOD)).isTrue();
|
||||
assertThat(NullableUtils.isNonNull(boolean.class, ElementType.PARAMETER)).isTrue();
|
||||
}
|
||||
|
||||
@Test // GH-3242
|
||||
void shouldConsiderVoidNullable() {
|
||||
|
||||
var method = ReflectionUtils.findMethod(NullableAnnotatedType.class, "noReturnActually");
|
||||
|
||||
assertThat(NullableUtils.isNonNull(method, ElementType.METHOD)).isFalse();
|
||||
}
|
||||
|
||||
@Test // GH-3242
|
||||
void shouldConsiderTypesNotAnnotatedWhereWeCannotDetermineThePackageAsNullable() throws ClassNotFoundException {
|
||||
|
||||
Class<?> typeInDefaultPackage = getClass().getClassLoader().loadClass("TypeInDefaultPackage");
|
||||
|
||||
assertThat(NullableUtils.isNonNull(typeInDefaultPackage, ElementType.PARAMETER)).isFalse();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,4 +34,8 @@ public interface NullableAnnotatedType {
|
||||
|
||||
@javax.annotation.Nonnull(when = When.MAYBE)
|
||||
String jsr305NullableReturnWhen();
|
||||
|
||||
boolean primitiveReturn();
|
||||
|
||||
void noReturnActually();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user