Skip to content

Commit d6d4124

Browse files
Fix nullability issues for primitives and types in default package.
See: #3242
1 parent 5f96594 commit d6d4124

File tree

3 files changed

+60
-7
lines changed

3 files changed

+60
-7
lines changed

src/main/java/org/springframework/data/util/NullableUtils.java

+30-7
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.springframework.core.annotation.AnnotatedElementUtils;
3434
import org.springframework.core.annotation.AnnotationUtils;
3535
import org.springframework.core.annotation.MergedAnnotations;
36+
import org.springframework.lang.Contract;
3637
import org.springframework.lang.NonNullApi;
3738
import org.springframework.lang.Nullable;
3839
import org.springframework.util.ClassUtils;
@@ -100,10 +101,21 @@ private NullableUtils() {}
100101
*
101102
* @param method the method to inspect.
102103
* @param elementType the element type.
103-
* @return {@literal true} if {@link ElementType} allows {@literal null} values by default.
104+
* @return {@literal false} if {@link ElementType} allows {@literal null} values by default. {@literal true} if the
105+
* methods return type is a {@link Class#isPrimitive() primitive} and {@literal false} if the method is
106+
* {@literal void}.
104107
* @see #isNonNull(Annotation, ElementType)
105108
*/
106109
public static boolean isNonNull(Method method, ElementType elementType) {
110+
111+
Class<?> returnType = method.getReturnType();
112+
113+
if (ReflectionUtils.isVoid(returnType)) {
114+
return false;
115+
} else if (returnType.isPrimitive()) {
116+
return true;
117+
}
118+
107119
return isNonNull(method.getDeclaringClass(), elementType) || isNonNull((AnnotatedElement) method, elementType);
108120
}
109121

@@ -114,10 +126,16 @@ public static boolean isNonNull(Method method, ElementType elementType) {
114126
*
115127
* @param type the class to inspect.
116128
* @param elementType the element type.
117-
* @return {@literal true} if {@link ElementType} allows {@literal null} values by default.
129+
* @return {@literal false} if {@link ElementType} allows {@literal null} values. {@literal true} the given
130+
* {@literal type} is a {@link Class#isPrimitive() primitive}.
118131
* @see #isNonNull(Annotation, ElementType)
119132
*/
120133
public static boolean isNonNull(Class<?> type, ElementType elementType) {
134+
135+
if (type.isPrimitive()) {
136+
return true;
137+
}
138+
121139
return isNonNull(type.getPackage(), elementType) || isNonNull((AnnotatedElement) type, elementType);
122140
}
123141

@@ -126,11 +144,17 @@ public static boolean isNonNull(Class<?> type, ElementType elementType) {
126144
* This method determines default {@code javax.annotation.Nonnull nullability} rules from the annotated element
127145
*
128146
* @param element the scope of declaration, may be a {@link Package}, {@link Class}, or
129-
* {@link java.lang.reflect.Method}.
147+
* {@link java.lang.reflect.Method}. Can be {@literal null}.
130148
* @param elementType the element type.
131-
* @return {@literal true} if {@link ElementType} allows {@literal null} values by default.
149+
* @return {@literal false} if {@link ElementType} allows {@literal null} values by default or if the given
150+
* {@link AnnotatedElement} is {@literal null}.
132151
*/
133-
public static boolean isNonNull(AnnotatedElement element, ElementType elementType) {
152+
@Contract("null, _ -> false")
153+
public static boolean isNonNull(@Nullable AnnotatedElement element, ElementType elementType) {
154+
155+
if (element == null) {
156+
return false;
157+
}
134158

135159
for (Annotation annotation : element.getAnnotations()) {
136160

@@ -157,8 +181,7 @@ private static boolean isNonNull(Annotation annotation, ElementType elementType)
157181
return true;
158182
}
159183

160-
if (!MergedAnnotations.from(annotation.annotationType()).isPresent(annotationClass)
161-
|| !isNonNull(annotation)) {
184+
if (!MergedAnnotations.from(annotation.annotationType()).isPresent(annotationClass) || !isNonNull(annotation)) {
162185
return false;
163186
}
164187

src/test/java/org/springframework/data/util/NullableUtilsUnitTests.java

+26
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
* Unit tests for {@link NullableUtils}.
3333
*
3434
* @author Mark Paluch
35+
* @author Christoph Strobl
3536
*/
3637
class NullableUtilsUnitTests {
3738

@@ -115,4 +116,29 @@ void shouldConsiderParametersJsr305NonnullAnnotation() {
115116

116117
assertThat(NullableUtils.isExplicitNullable(new MethodParameter(method, -1))).isTrue();
117118
}
119+
120+
@Test // GH-3242
121+
void shouldConsiderPrimitivesNonNullable() {
122+
123+
var method = ReflectionUtils.findMethod(NullableAnnotatedType.class, "primitiveReturn");
124+
125+
assertThat(NullableUtils.isNonNull(method, ElementType.METHOD)).isTrue();
126+
assertThat(NullableUtils.isNonNull(boolean.class, ElementType.PARAMETER)).isTrue();
127+
}
128+
129+
@Test // GH-3242
130+
void shouldConsiderVoidNullable() {
131+
132+
var method = ReflectionUtils.findMethod(NullableAnnotatedType.class, "noReturnActually");
133+
134+
assertThat(NullableUtils.isNonNull(method, ElementType.METHOD)).isFalse();
135+
}
136+
137+
@Test // GH-3242
138+
void shouldConsiderTypesNotAnnotatedWhereWeCannotDetermineThePackageAsNullable() throws ClassNotFoundException {
139+
140+
Class<?> typeInDefaultPackage = getClass().getClassLoader().loadClass("TypeInDefaultPackage");
141+
142+
assertThat(NullableUtils.isNonNull(typeInDefaultPackage, ElementType.PARAMETER)).isFalse();
143+
}
118144
}

src/test/java/org/springframework/data/util/nonnull/NullableAnnotatedType.java

+4
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,8 @@ public interface NullableAnnotatedType {
3434

3535
@javax.annotation.Nonnull(when = When.MAYBE)
3636
String jsr305NullableReturnWhen();
37+
38+
boolean primitiveReturn();
39+
40+
void noReturnActually();
3741
}

0 commit comments

Comments
 (0)