Browse Source

Specify generic type nullness in spring-core

Also in spring-core-test.

See gh-34140
pull/34266/head
Sébastien Deleuze 11 months ago
parent
commit
9d9383aaeb
  1. 6
      spring-core-test/src/main/java/org/springframework/core/test/tools/CompileWithForkedClassLoaderClassLoader.java
  2. 4
      spring-core-test/src/main/java/org/springframework/core/test/tools/DynamicClassLoader.java
  3. 4
      spring-core/src/main/java/org/springframework/aot/generate/MethodReference.java
  4. 6
      spring-core/src/main/java/org/springframework/core/MethodParameter.java
  5. 15
      spring-core/src/main/java/org/springframework/core/ReactiveAdapterRegistry.java
  6. 6
      spring-core/src/main/java/org/springframework/core/ResolvableType.java
  7. 4
      spring-core/src/main/java/org/springframework/core/SerializableTypeWrapper.java
  8. 6
      spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java
  9. 14
      spring-core/src/main/java/org/springframework/core/annotation/AnnotationAttributes.java
  10. 6
      spring-core/src/main/java/org/springframework/core/annotation/AnnotationTypeMappings.java
  11. 6
      spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java
  12. 4
      spring-core/src/main/java/org/springframework/core/annotation/AnnotationsProcessor.java
  13. 32
      spring-core/src/main/java/org/springframework/core/annotation/AnnotationsScanner.java
  14. 4
      spring-core/src/main/java/org/springframework/core/annotation/AttributeMethods.java
  15. 16
      spring-core/src/main/java/org/springframework/core/annotation/MergedAnnotationCollectors.java
  16. 11
      spring-core/src/main/java/org/springframework/core/annotation/MergedAnnotationPredicates.java
  17. 17
      spring-core/src/main/java/org/springframework/core/annotation/TypeMappedAnnotations.java
  18. 6
      spring-core/src/main/java/org/springframework/core/io/support/SpringFactoriesLoader.java
  19. 12
      spring-core/src/main/java/org/springframework/core/type/AnnotatedTypeMetadata.java
  20. 7
      spring-core/src/main/java/org/springframework/core/type/StandardAnnotationMetadata.java
  21. 7
      spring-core/src/main/java/org/springframework/core/type/StandardMethodMetadata.java
  22. 6
      spring-core/src/main/java/org/springframework/util/CollectionUtils.java
  23. 10
      spring-core/src/main/java/org/springframework/util/CompositeCollection.java
  24. 12
      spring-core/src/main/java/org/springframework/util/ConcurrentReferenceHashMap.java
  25. 12
      spring-core/src/main/java/org/springframework/util/MethodInvoker.java
  26. 4
      spring-core/src/main/java/org/springframework/util/MultiValueMap.java
  27. 10
      spring-core/src/main/java/org/springframework/util/ObjectUtils.java
  28. 6
      spring-core/src/main/java/org/springframework/util/StringUtils.java
  29. 21
      spring-core/src/main/java/org/springframework/util/function/SingletonSupplier.java
  30. 5
      spring-core/src/main/java/org/springframework/util/function/SupplierUtils.java
  31. 15
      spring-core/src/test/java/org/springframework/core/annotation/AnnotationsScannerTests.java

6
spring-core-test/src/main/java/org/springframework/core/test/tools/CompileWithForkedClassLoaderClassLoader.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2023 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -37,7 +37,7 @@ final class CompileWithForkedClassLoaderClassLoader extends ClassLoader {
private final ClassLoader testClassLoader; private final ClassLoader testClassLoader;
private Function<String, byte[]> classResourceLookup = name -> null; private Function<String, byte @Nullable []> classResourceLookup = name -> null;
public CompileWithForkedClassLoaderClassLoader(ClassLoader testClassLoader) { public CompileWithForkedClassLoaderClassLoader(ClassLoader testClassLoader) {
@ -48,7 +48,7 @@ final class CompileWithForkedClassLoaderClassLoader extends ClassLoader {
// Invoked reflectively by DynamicClassLoader // Invoked reflectively by DynamicClassLoader
@SuppressWarnings("unused") @SuppressWarnings("unused")
void setClassResourceLookup(Function<String, byte[]> classResourceLookup) { void setClassResourceLookup(Function<String, byte @Nullable []> classResourceLookup) {
this.classResourceLookup = classResourceLookup; this.classResourceLookup = classResourceLookup;
} }

4
spring-core-test/src/main/java/org/springframework/core/test/tools/DynamicClassLoader.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2024 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -71,7 +71,7 @@ public class DynamicClassLoader extends ClassLoader {
"setClassResourceLookup", Function.class); "setClassResourceLookup", Function.class);
ReflectionUtils.makeAccessible(setClassResourceLookupMethod); ReflectionUtils.makeAccessible(setClassResourceLookupMethod);
ReflectionUtils.invokeMethod(setClassResourceLookupMethod, ReflectionUtils.invokeMethod(setClassResourceLookupMethod,
getParent(), (Function<String, byte[]>) this::findClassBytes); getParent(), (Function<String, byte @Nullable []>) this::findClassBytes);
this.defineClassMethod = lookupMethod(parentClass, this.defineClassMethod = lookupMethod(parentClass,
"defineDynamicClass", String.class, byte[].class, int.class, int.class); "defineDynamicClass", String.class, byte[].class, int.class, int.class);
ReflectionUtils.makeAccessible(this.defineClassMethod); ReflectionUtils.makeAccessible(this.defineClassMethod);

4
spring-core/src/main/java/org/springframework/aot/generate/MethodReference.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2022 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -106,7 +106,7 @@ public interface MethodReference {
* @param function the resolver function * @param function the resolver function
* @return a new {@link ArgumentCodeGenerator} instance backed by the function * @return a new {@link ArgumentCodeGenerator} instance backed by the function
*/ */
static ArgumentCodeGenerator from(Function<TypeName, CodeBlock> function) { static ArgumentCodeGenerator from(Function<TypeName, @Nullable CodeBlock> function) {
return function::apply; return function::apply;
} }

6
spring-core/src/main/java/org/springframework/core/MethodParameter.java

@ -706,7 +706,7 @@ public class MethodParameter {
} }
ParameterNameDiscoverer discoverer = this.parameterNameDiscoverer; ParameterNameDiscoverer discoverer = this.parameterNameDiscoverer;
if (discoverer != null) { if (discoverer != null) {
String[] parameterNames = null; @Nullable String[] parameterNames = null;
if (this.executable instanceof Method method) { if (this.executable instanceof Method method) {
parameterNames = discoverer.getParameterNames(method); parameterNames = discoverer.getParameterNames(method);
} }
@ -865,7 +865,7 @@ public class MethodParameter {
* @return the corresponding MethodParameter instance * @return the corresponding MethodParameter instance
* @since 6.1 * @since 6.1
*/ */
public static MethodParameter forFieldAwareConstructor(Constructor<?> ctor, int parameterIndex, String fieldName) { public static MethodParameter forFieldAwareConstructor(Constructor<?> ctor, int parameterIndex, @Nullable String fieldName) {
return new FieldAwareConstructorParameter(ctor, parameterIndex, fieldName); return new FieldAwareConstructorParameter(ctor, parameterIndex, fieldName);
} }
@ -877,7 +877,7 @@ public class MethodParameter {
private volatile Annotation @Nullable [] combinedAnnotations; private volatile Annotation @Nullable [] combinedAnnotations;
public FieldAwareConstructorParameter(Constructor<?> constructor, int parameterIndex, String fieldName) { public FieldAwareConstructorParameter(Constructor<?> constructor, int parameterIndex, @Nullable String fieldName) {
super(constructor, parameterIndex); super(constructor, parameterIndex);
this.parameterName = fieldName; this.parameterName = fieldName;
} }

15
spring-core/src/main/java/org/springframework/core/ReactiveAdapterRegistry.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2024 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -19,12 +19,15 @@ package org.springframework.core;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage; import java.util.concurrent.CompletionStage;
import java.util.concurrent.Flow; import java.util.concurrent.Flow;
import java.util.function.Function; import java.util.function.Function;
import io.smallrye.mutiny.Multi;
import io.smallrye.mutiny.Uni;
import org.jspecify.annotations.Nullable; import org.jspecify.annotations.Nullable;
import org.reactivestreams.FlowAdapters; import org.reactivestreams.FlowAdapters;
import org.reactivestreams.Publisher; import org.reactivestreams.Publisher;
@ -380,13 +383,13 @@ public class ReactiveAdapterRegistry {
io.smallrye.mutiny.groups.MultiCreate.class, "publisher", Flow.Publisher.class); io.smallrye.mutiny.groups.MultiCreate.class, "publisher", Flow.Publisher.class);
registry.registerReactiveType(uniDesc, registry.registerReactiveType(uniDesc,
uni -> FlowAdapters.toPublisher((Flow.Publisher<Object>) uni -> FlowAdapters.toPublisher((Flow.Publisher<Object>)
ReflectionUtils.invokeMethod(uniToPublisher, ((io.smallrye.mutiny.Uni<?>) uni).convert())), Objects.requireNonNull(ReflectionUtils.invokeMethod(uniToPublisher, ((Uni<?>) uni).convert()))),
publisher -> ReflectionUtils.invokeMethod(uniPublisher, io.smallrye.mutiny.Uni.createFrom(), publisher -> Objects.requireNonNull(ReflectionUtils.invokeMethod(uniPublisher, Uni.createFrom(),
FlowAdapters.toFlowPublisher(publisher))); FlowAdapters.toFlowPublisher(publisher))));
registry.registerReactiveType(multiDesc, registry.registerReactiveType(multiDesc,
multi -> FlowAdapters.toPublisher((Flow.Publisher<Object>) multi), multi -> FlowAdapters.toPublisher((Flow.Publisher<Object>) multi),
publisher -> ReflectionUtils.invokeMethod(multiPublisher, io.smallrye.mutiny.Multi.createFrom(), publisher -> Objects.requireNonNull(ReflectionUtils.invokeMethod(multiPublisher, Multi.createFrom(),
FlowAdapters.toFlowPublisher(publisher))); FlowAdapters.toFlowPublisher(publisher))));
} }
else { else {
// Mutiny 1 based on Reactive Streams // Mutiny 1 based on Reactive Streams

6
spring-core/src/main/java/org/springframework/core/ResolvableType.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2024 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -825,9 +825,9 @@ public class ResolvableType implements Serializable {
* @see #getGenerics() * @see #getGenerics()
* @see #resolve() * @see #resolve()
*/ */
public Class<?>[] resolveGenerics() { public @Nullable Class<?>[] resolveGenerics() {
ResolvableType[] generics = getGenerics(); ResolvableType[] generics = getGenerics();
Class<?>[] resolvedGenerics = new Class<?>[generics.length]; @Nullable Class<?>[] resolvedGenerics = new Class<?>[generics.length];
for (int i = 0; i < generics.length; i++) { for (int i = 0; i < generics.length; i++) {
resolvedGenerics[i] = generics[i].resolve(); resolvedGenerics[i] = generics[i].resolve();
} }

4
spring-core/src/main/java/org/springframework/core/SerializableTypeWrapper.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2024 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -205,7 +205,7 @@ final class SerializableTypeWrapper {
if (returnValue == null) { if (returnValue == null) {
return null; return null;
} }
Type[] result = new Type[((Type[]) returnValue).length]; @Nullable Type[] result = new Type[((Type[]) returnValue).length];
for (int i = 0; i < result.length; i++) { for (int i = 0; i < result.length; i++) {
result[i] = forTypeProvider(new MethodInvokeTypeProvider(this.provider, method, i)); result[i] = forTypeProvider(new MethodInvokeTypeProvider(this.provider, method, i));
} }

6
spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2022 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -478,7 +478,7 @@ public abstract class AnnotatedElementUtils {
* attributes from all annotations found, or {@code null} if not found * attributes from all annotations found, or {@code null} if not found
* @see #getAllAnnotationAttributes(AnnotatedElement, String, boolean, boolean) * @see #getAllAnnotationAttributes(AnnotatedElement, String, boolean, boolean)
*/ */
public static @Nullable MultiValueMap<String, Object> getAllAnnotationAttributes( public static MultiValueMap<String, @Nullable Object> getAllAnnotationAttributes(
AnnotatedElement element, String annotationName) { AnnotatedElement element, String annotationName) {
return getAllAnnotationAttributes(element, annotationName, false, false); return getAllAnnotationAttributes(element, annotationName, false, false);
@ -502,7 +502,7 @@ public abstract class AnnotatedElementUtils {
* @return a {@link MultiValueMap} keyed by attribute name, containing the annotation * @return a {@link MultiValueMap} keyed by attribute name, containing the annotation
* attributes from all annotations found, or {@code null} if not found * attributes from all annotations found, or {@code null} if not found
*/ */
public static @Nullable MultiValueMap<String, Object> getAllAnnotationAttributes(AnnotatedElement element, public static MultiValueMap<String, @Nullable Object> getAllAnnotationAttributes(AnnotatedElement element,
String annotationName, final boolean classValuesAsString, final boolean nestedAnnotationsAsMap) { String annotationName, final boolean classValuesAsString, final boolean nestedAnnotationsAsMap) {
Adapt[] adaptations = Adapt.values(classValuesAsString, nestedAnnotationsAsMap); Adapt[] adaptations = Adapt.values(classValuesAsString, nestedAnnotationsAsMap);

14
spring-core/src/main/java/org/springframework/core/annotation/AnnotationAttributes.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2024 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -45,7 +45,7 @@ import org.springframework.util.StringUtils;
* @see AnnotatedElementUtils * @see AnnotatedElementUtils
*/ */
@SuppressWarnings("serial") @SuppressWarnings("serial")
public class AnnotationAttributes extends LinkedHashMap<String, Object> { public class AnnotationAttributes extends LinkedHashMap<String, @Nullable Object> {
private static final String UNKNOWN = "unknown"; private static final String UNKNOWN = "unknown";
@ -83,7 +83,7 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> {
* @param map original source of annotation attribute <em>key-value</em> pairs * @param map original source of annotation attribute <em>key-value</em> pairs
* @see #fromMap(Map) * @see #fromMap(Map)
*/ */
public AnnotationAttributes(Map<String, Object> map) { public AnnotationAttributes(Map<String, @Nullable Object> map) {
super(map); super(map);
this.annotationType = null; this.annotationType = null;
this.displayName = UNKNOWN; this.displayName = UNKNOWN;
@ -376,10 +376,10 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> {
@Override @Override
public String toString() { public String toString() {
Iterator<Map.Entry<String, Object>> entries = entrySet().iterator(); Iterator<Map.Entry<String, @Nullable Object>> entries = entrySet().iterator();
StringBuilder sb = new StringBuilder("{"); StringBuilder sb = new StringBuilder("{");
while (entries.hasNext()) { while (entries.hasNext()) {
Map.Entry<String, Object> entry = entries.next(); Map.Entry<String, @Nullable Object> entry = entries.next();
sb.append(entry.getKey()); sb.append(entry.getKey());
sb.append('='); sb.append('=');
sb.append(valueToString(entry.getValue())); sb.append(valueToString(entry.getValue()));
@ -391,7 +391,7 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> {
return sb.toString(); return sb.toString();
} }
private String valueToString(Object value) { private String valueToString(@Nullable Object value) {
if (value == this) { if (value == this) {
return "(this Map)"; return "(this Map)";
} }
@ -410,7 +410,7 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> {
* to the {@link #AnnotationAttributes(Map)} constructor. * to the {@link #AnnotationAttributes(Map)} constructor.
* @param map original source of annotation attribute <em>key-value</em> pairs * @param map original source of annotation attribute <em>key-value</em> pairs
*/ */
public static @Nullable AnnotationAttributes fromMap(@Nullable Map<String, Object> map) { public static @Nullable AnnotationAttributes fromMap(@Nullable Map<String, @Nullable Object> map) {
if (map == null) { if (map == null) {
return null; return null;
} }

6
spring-core/src/main/java/org/springframework/core/annotation/AnnotationTypeMappings.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2023 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -27,6 +27,7 @@ import java.util.Set;
import org.jspecify.annotations.Nullable; import org.jspecify.annotations.Nullable;
import org.springframework.lang.Contract;
import org.springframework.util.ConcurrentReferenceHashMap; import org.springframework.util.ConcurrentReferenceHashMap;
/** /**
@ -87,7 +88,7 @@ final class AnnotationTypeMappings {
} }
private void addMetaAnnotationsToQueue(Deque<AnnotationTypeMapping> queue, AnnotationTypeMapping source) { private void addMetaAnnotationsToQueue(Deque<AnnotationTypeMapping> queue, AnnotationTypeMapping source) {
Annotation[] metaAnnotations = AnnotationsScanner.getDeclaredAnnotations(source.getAnnotationType(), false); @Nullable Annotation[] metaAnnotations = AnnotationsScanner.getDeclaredAnnotations(source.getAnnotationType(), false);
for (Annotation metaAnnotation : metaAnnotations) { for (Annotation metaAnnotation : metaAnnotations) {
if (!isMappable(source, metaAnnotation)) { if (!isMappable(source, metaAnnotation)) {
continue; continue;
@ -127,6 +128,7 @@ final class AnnotationTypeMappings {
} }
} }
@Contract("_, null -> false")
private boolean isMappable(AnnotationTypeMapping source, @Nullable Annotation metaAnnotation) { private boolean isMappable(AnnotationTypeMapping source, @Nullable Annotation metaAnnotation) {
return (metaAnnotation != null && !this.filter.matches(metaAnnotation) && return (metaAnnotation != null && !this.filter.matches(metaAnnotation) &&
!AnnotationFilter.PLAIN.matches(source.getAnnotationType()) && !AnnotationFilter.PLAIN.matches(source.getAnnotationType()) &&

6
spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2024 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -773,7 +773,7 @@ public abstract class AnnotationUtils {
* @see #getAnnotationAttributes(Annotation, boolean, boolean) * @see #getAnnotationAttributes(Annotation, boolean, boolean)
* @see #getAnnotationAttributes(AnnotatedElement, Annotation, boolean, boolean) * @see #getAnnotationAttributes(AnnotatedElement, Annotation, boolean, boolean)
*/ */
public static Map<String, Object> getAnnotationAttributes(Annotation annotation) { public static Map<String, @Nullable Object> getAnnotationAttributes(Annotation annotation) {
return getAnnotationAttributes(null, annotation); return getAnnotationAttributes(null, annotation);
} }
@ -791,7 +791,7 @@ public abstract class AnnotationUtils {
* corresponding attribute values as values (never {@code null}) * corresponding attribute values as values (never {@code null})
* @see #getAnnotationAttributes(Annotation, boolean, boolean) * @see #getAnnotationAttributes(Annotation, boolean, boolean)
*/ */
public static Map<String, Object> getAnnotationAttributes( public static Map<String, @Nullable Object> getAnnotationAttributes(
Annotation annotation, boolean classValuesAsString) { Annotation annotation, boolean classValuesAsString) {
return getAnnotationAttributes(annotation, classValuesAsString, false); return getAnnotationAttributes(annotation, classValuesAsString, false);

4
spring-core/src/main/java/org/springframework/core/annotation/AnnotationsProcessor.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2021 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -54,7 +54,7 @@ interface AnnotationsProcessor<C, R> {
* {@code null} elements) * {@code null} elements)
* @return a {@code non-null} result if no further processing is required * @return a {@code non-null} result if no further processing is required
*/ */
@Nullable R doWithAnnotations(C context, int aggregateIndex, @Nullable Object source, Annotation[] annotations); @Nullable R doWithAnnotations(C context, int aggregateIndex, @Nullable Object source, @Nullable Annotation[] annotations);
/** /**
* Get the final result to be returned. By default this method returns * Get the final result to be returned. By default this method returns

32
spring-core/src/main/java/org/springframework/core/annotation/AnnotationsScanner.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2024 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -47,7 +47,7 @@ import org.springframework.util.ReflectionUtils;
*/ */
abstract class AnnotationsScanner { abstract class AnnotationsScanner {
private static final Annotation[] NO_ANNOTATIONS = {}; private static final @Nullable Annotation[] NO_ANNOTATIONS = {};
private static final Method[] NO_METHODS = {}; private static final Method[] NO_METHODS = {};
@ -55,7 +55,7 @@ abstract class AnnotationsScanner {
private static final Map<AnnotatedElement, Annotation[]> declaredAnnotationCache = private static final Map<AnnotatedElement, Annotation[]> declaredAnnotationCache =
new ConcurrentReferenceHashMap<>(256); new ConcurrentReferenceHashMap<>(256);
private static final Map<Class<?>, Method[]> baseTypeMethodsCache = private static final Map<Class<?>, @Nullable Method[]> baseTypeMethodsCache =
new ConcurrentReferenceHashMap<>(256); new ConcurrentReferenceHashMap<>(256);
@ -114,7 +114,7 @@ abstract class AnnotationsScanner {
if (isWithoutHierarchy(source, Search.never)) { if (isWithoutHierarchy(source, Search.never)) {
return processElement(context, source, processor); return processElement(context, source, processor);
} }
Annotation[] relevant = null; @Nullable Annotation[] relevant = null;
int remaining = Integer.MAX_VALUE; int remaining = Integer.MAX_VALUE;
int aggregateIndex = 0; int aggregateIndex = 0;
Class<?> root = source; Class<?> root = source;
@ -123,7 +123,7 @@ abstract class AnnotationsScanner {
if (result != null) { if (result != null) {
return result; return result;
} }
Annotation[] declaredAnns = getDeclaredAnnotations(source, true); @Nullable Annotation[] declaredAnns = getDeclaredAnnotations(source, true);
if (declaredAnns.length > 0) { if (declaredAnns.length > 0) {
if (relevant == null) { if (relevant == null) {
relevant = root.getAnnotations(); relevant = root.getAnnotations();
@ -133,6 +133,7 @@ abstract class AnnotationsScanner {
if (declaredAnns[i] != null) { if (declaredAnns[i] != null) {
boolean isRelevant = false; boolean isRelevant = false;
for (int relevantIndex = 0; relevantIndex < relevant.length; relevantIndex++) { for (int relevantIndex = 0; relevantIndex < relevant.length; relevantIndex++) {
//noinspection DataFlowIssue
if (relevant[relevantIndex] != null && if (relevant[relevantIndex] != null &&
declaredAnns[i].annotationType() == relevant[relevantIndex].annotationType()) { declaredAnns[i].annotationType() == relevant[relevantIndex].annotationType()) {
isRelevant = true; isRelevant = true;
@ -181,7 +182,7 @@ abstract class AnnotationsScanner {
if (hasPlainJavaAnnotationsOnly(source)) { if (hasPlainJavaAnnotationsOnly(source)) {
return null; return null;
} }
Annotation[] annotations = getDeclaredAnnotations(source, false); @Nullable Annotation[] annotations = getDeclaredAnnotations(source, false);
result = processor.doWithAnnotations(context, aggregateIndex[0], source, annotations); result = processor.doWithAnnotations(context, aggregateIndex[0], source, annotations);
if (result != null) { if (result != null) {
return result; return result;
@ -320,16 +321,18 @@ abstract class AnnotationsScanner {
return null; return null;
} }
private static <C> Method[] getBaseTypeMethods(C context, Class<?> baseType) { @SuppressWarnings("NullAway") // Dataflow analysis limitation
private static <C> @Nullable Method[] getBaseTypeMethods(C context, Class<?> baseType) {
if (baseType == Object.class || hasPlainJavaAnnotationsOnly(baseType)) { if (baseType == Object.class || hasPlainJavaAnnotationsOnly(baseType)) {
return NO_METHODS; return NO_METHODS;
} }
Method[] methods = baseTypeMethodsCache.get(baseType); @Nullable Method[] methods = baseTypeMethodsCache.get(baseType);
if (methods == null) { if (methods == null) {
methods = ReflectionUtils.getDeclaredMethods(baseType); methods = ReflectionUtils.getDeclaredMethods(baseType);
int cleared = 0; int cleared = 0;
for (int i = 0; i < methods.length; i++) { for (int i = 0; i < methods.length; i++) {
//noinspection DataFlowIssue
if (Modifier.isPrivate(methods[i].getModifiers()) || if (Modifier.isPrivate(methods[i].getModifiers()) ||
hasPlainJavaAnnotationsOnly(methods[i]) || hasPlainJavaAnnotationsOnly(methods[i]) ||
getDeclaredAnnotations(methods[i], false).length == 0) { getDeclaredAnnotations(methods[i], false).length == 0) {
@ -385,14 +388,14 @@ abstract class AnnotationsScanner {
private static <C, R> @Nullable R processMethodAnnotations(C context, int aggregateIndex, Method source, private static <C, R> @Nullable R processMethodAnnotations(C context, int aggregateIndex, Method source,
AnnotationsProcessor<C, R> processor) { AnnotationsProcessor<C, R> processor) {
Annotation[] annotations = getDeclaredAnnotations(source, false); @Nullable Annotation[] annotations = getDeclaredAnnotations(source, false);
R result = processor.doWithAnnotations(context, aggregateIndex, source, annotations); R result = processor.doWithAnnotations(context, aggregateIndex, source, annotations);
if (result != null) { if (result != null) {
return result; return result;
} }
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(source); Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(source);
if (bridgedMethod != source) { if (bridgedMethod != source) {
Annotation[] bridgedAnnotations = getDeclaredAnnotations(bridgedMethod, true); @Nullable Annotation[] bridgedAnnotations = getDeclaredAnnotations(bridgedMethod, true);
for (int i = 0; i < bridgedAnnotations.length; i++) { for (int i = 0; i < bridgedAnnotations.length; i++) {
if (ObjectUtils.containsElement(annotations, bridgedAnnotations[i])) { if (ObjectUtils.containsElement(annotations, bridgedAnnotations[i])) {
bridgedAnnotations[i] = null; bridgedAnnotations[i] = null;
@ -419,7 +422,7 @@ abstract class AnnotationsScanner {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
static <A extends Annotation> @Nullable A getDeclaredAnnotation(AnnotatedElement source, Class<A> annotationType) { static <A extends Annotation> @Nullable A getDeclaredAnnotation(AnnotatedElement source, Class<A> annotationType) {
Annotation[] annotations = getDeclaredAnnotations(source, false); @Nullable Annotation[] annotations = getDeclaredAnnotations(source, false);
for (Annotation annotation : annotations) { for (Annotation annotation : annotations) {
if (annotation != null && annotationType == annotation.annotationType()) { if (annotation != null && annotationType == annotation.annotationType()) {
return (A) annotation; return (A) annotation;
@ -428,9 +431,10 @@ abstract class AnnotationsScanner {
return null; return null;
} }
static Annotation[] getDeclaredAnnotations(AnnotatedElement source, boolean defensive) { @SuppressWarnings("NullAway") // Dataflow analysis limitation
static @Nullable Annotation[] getDeclaredAnnotations(AnnotatedElement source, boolean defensive) {
boolean cached = false; boolean cached = false;
Annotation[] annotations = declaredAnnotationCache.get(source); @Nullable Annotation[] annotations = declaredAnnotationCache.get(source);
if (annotations != null) { if (annotations != null) {
cached = true; cached = true;
} }
@ -440,6 +444,7 @@ abstract class AnnotationsScanner {
boolean allIgnored = true; boolean allIgnored = true;
for (int i = 0; i < annotations.length; i++) { for (int i = 0; i < annotations.length; i++) {
Annotation annotation = annotations[i]; Annotation annotation = annotations[i];
//noinspection DataFlowIssue
if (isIgnorable(annotation.annotationType()) || if (isIgnorable(annotation.annotationType()) ||
!AttributeMethods.forAnnotationType(annotation.annotationType()).canLoad(annotation)) { !AttributeMethods.forAnnotationType(annotation.annotationType()).canLoad(annotation)) {
annotations[i] = null; annotations[i] = null;
@ -450,6 +455,7 @@ abstract class AnnotationsScanner {
} }
annotations = (allIgnored ? NO_ANNOTATIONS : annotations); annotations = (allIgnored ? NO_ANNOTATIONS : annotations);
if (source instanceof Class || source instanceof Member) { if (source instanceof Class || source instanceof Member) {
//noinspection NullableProblems
declaredAnnotationCache.put(source, annotations); declaredAnnotationCache.put(source, annotations);
cached = true; cached = true;
} }

4
spring-core/src/main/java/org/springframework/core/annotation/AttributeMethods.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2024 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -250,11 +250,13 @@ final class AttributeMethods {
return cache.computeIfAbsent(annotationType, AttributeMethods::compute); return cache.computeIfAbsent(annotationType, AttributeMethods::compute);
} }
@SuppressWarnings("NullAway") // Dataflow analysis limitation
private static AttributeMethods compute(Class<? extends Annotation> annotationType) { private static AttributeMethods compute(Class<? extends Annotation> annotationType) {
Method[] methods = annotationType.getDeclaredMethods(); Method[] methods = annotationType.getDeclaredMethods();
int size = methods.length; int size = methods.length;
for (int i = 0; i < methods.length; i++) { for (int i = 0; i < methods.length; i++) {
if (!isAttributeMethod(methods[i])) { if (!isAttributeMethod(methods[i])) {
//noinspection DataFlowIssue
methods[i] = null; methods[i] = null;
size--; size--;
} }

16
spring-core/src/main/java/org/springframework/core/annotation/MergedAnnotationCollectors.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2020 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -26,6 +26,8 @@ import java.util.function.IntFunction;
import java.util.stream.Collector; import java.util.stream.Collector;
import java.util.stream.Collector.Characteristics; import java.util.stream.Collector.Characteristics;
import org.jspecify.annotations.Nullable;
import org.springframework.core.annotation.MergedAnnotation.Adapt; import org.springframework.core.annotation.MergedAnnotation.Adapt;
import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
@ -108,10 +110,10 @@ public abstract class MergedAnnotationCollectors {
* annotations into a {@link LinkedMultiValueMap} * annotations into a {@link LinkedMultiValueMap}
* @see #toMultiValueMap(Function, MergedAnnotation.Adapt...) * @see #toMultiValueMap(Function, MergedAnnotation.Adapt...)
*/ */
public static <A extends Annotation> Collector<MergedAnnotation<A>, ?, MultiValueMap<String, Object>> toMultiValueMap( public static <A extends Annotation> Collector<MergedAnnotation<A>, ? extends @Nullable Object, @Nullable MultiValueMap<String, @Nullable Object>> toMultiValueMap(
Adapt... adaptations) { Adapt... adaptations) {
return toMultiValueMap(Function.identity(), adaptations); return toMultiValueMap((MultiValueMap<String, @Nullable Object> t) -> t, adaptations);
} }
/** /**
@ -126,14 +128,14 @@ public abstract class MergedAnnotationCollectors {
* annotations into a {@link LinkedMultiValueMap} * annotations into a {@link LinkedMultiValueMap}
* @see #toMultiValueMap(MergedAnnotation.Adapt...) * @see #toMultiValueMap(MergedAnnotation.Adapt...)
*/ */
public static <A extends Annotation> Collector<MergedAnnotation<A>, ?, MultiValueMap<String, Object>> toMultiValueMap( public static <A extends Annotation> Collector<MergedAnnotation<A>, ? extends @Nullable Object, @Nullable MultiValueMap<String, @Nullable Object>> toMultiValueMap(
Function<MultiValueMap<String, Object>, MultiValueMap<String, Object>> finisher, Function<MultiValueMap<String, @Nullable Object>, @Nullable MultiValueMap<String, @Nullable Object>> finisher,
Adapt... adaptations) { Adapt... adaptations) {
Characteristics[] characteristics = (isSameInstance(finisher, Function.identity()) ? Characteristics[] characteristics = (isSameInstance(finisher, Function.identity()) ?
IDENTITY_FINISH_CHARACTERISTICS : NO_CHARACTERISTICS); IDENTITY_FINISH_CHARACTERISTICS : NO_CHARACTERISTICS);
return Collector.of(LinkedMultiValueMap::new, return Collector.of(LinkedMultiValueMap::new,
(map, annotation) -> annotation.asMap(adaptations).forEach(map::add), (MultiValueMap<String, @Nullable Object> map, MergedAnnotation<A> annotation) -> annotation.asMap(adaptations).forEach(map::add),
MergedAnnotationCollectors::combiner, finisher, characteristics); MergedAnnotationCollectors::combiner, finisher, characteristics);
} }
@ -157,7 +159,7 @@ public abstract class MergedAnnotationCollectors {
* <p>This method is only invoked if the {@link java.util.stream.Stream} is * <p>This method is only invoked if the {@link java.util.stream.Stream} is
* processed in {@linkplain java.util.stream.Stream#parallel() parallel}. * processed in {@linkplain java.util.stream.Stream#parallel() parallel}.
*/ */
private static <K, V> MultiValueMap<K, V> combiner(MultiValueMap<K, V> map, MultiValueMap<K, V> additions) { private static <K, V> MultiValueMap<K, @Nullable V> combiner(MultiValueMap<K, @Nullable V> map, MultiValueMap<K, @Nullable V> additions) {
map.addAll(additions); map.addAll(additions);
return map; return map;
} }

11
spring-core/src/main/java/org/springframework/core/annotation/MergedAnnotationPredicates.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2022 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -23,8 +23,6 @@ import java.util.Set;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate; import java.util.function.Predicate;
import org.jspecify.annotations.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
@ -126,7 +124,8 @@ public abstract class MergedAnnotationPredicates {
private boolean hasLastValue; private boolean hasLastValue;
private @Nullable Object lastValue; @SuppressWarnings("NullAway.Init")
private Object lastValue;
FirstRunOfPredicate(Function<? super MergedAnnotation<A>, ?> valueExtractor) { FirstRunOfPredicate(Function<? super MergedAnnotation<A>, ?> valueExtractor) {
Assert.notNull(valueExtractor, "Value extractor must not be null"); Assert.notNull(valueExtractor, "Value extractor must not be null");
@ -134,7 +133,7 @@ public abstract class MergedAnnotationPredicates {
} }
@Override @Override
public boolean test(@Nullable MergedAnnotation<A> annotation) { public boolean test(MergedAnnotation<A> annotation) {
if (!this.hasLastValue) { if (!this.hasLastValue) {
this.hasLastValue = true; this.hasLastValue = true;
this.lastValue = this.valueExtractor.apply(annotation); this.lastValue = this.valueExtractor.apply(annotation);
@ -162,7 +161,7 @@ public abstract class MergedAnnotationPredicates {
} }
@Override @Override
public boolean test(@Nullable MergedAnnotation<A> annotation) { public boolean test(MergedAnnotation<A> annotation) {
K key = this.keyExtractor.apply(annotation); K key = this.keyExtractor.apply(annotation);
return this.seen.add(key); return this.seen.add(key);
} }

17
spring-core/src/main/java/org/springframework/core/annotation/TypeMappedAnnotations.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2024 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -29,6 +29,7 @@ import java.util.function.Predicate;
import java.util.stream.Stream; import java.util.stream.Stream;
import java.util.stream.StreamSupport; import java.util.stream.StreamSupport;
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable; import org.jspecify.annotations.Nullable;
/** /**
@ -309,7 +310,7 @@ final class TypeMappedAnnotations implements MergedAnnotations {
@Override @Override
public @Nullable Boolean doWithAnnotations(Object requiredType, int aggregateIndex, public @Nullable Boolean doWithAnnotations(Object requiredType, int aggregateIndex,
@Nullable Object source, Annotation[] annotations) { @Nullable Object source, @Nullable Annotation[] annotations) {
for (Annotation annotation : annotations) { for (Annotation annotation : annotations) {
if (annotation != null) { if (annotation != null) {
@ -388,7 +389,7 @@ final class TypeMappedAnnotations implements MergedAnnotations {
@Override @Override
public @Nullable MergedAnnotation<A> doWithAnnotations(Object type, int aggregateIndex, public @Nullable MergedAnnotation<A> doWithAnnotations(Object type, int aggregateIndex,
@Nullable Object source, Annotation[] annotations) { @Nullable Object source, @Nullable Annotation[] annotations) {
for (Annotation annotation : annotations) { for (Annotation annotation : annotations) {
if (annotation != null && !annotationFilter.matches(annotation)) { if (annotation != null && !annotationFilter.matches(annotation)) {
@ -450,24 +451,24 @@ final class TypeMappedAnnotations implements MergedAnnotations {
@Override @Override
public @Nullable List<Aggregate> doWithAnnotations(Object criteria, int aggregateIndex, public @Nullable List<Aggregate> doWithAnnotations(Object criteria, int aggregateIndex,
@Nullable Object source, Annotation[] annotations) { @Nullable Object source, @Nullable Annotation[] annotations) {
this.aggregates.add(createAggregate(aggregateIndex, source, annotations)); this.aggregates.add(createAggregate(aggregateIndex, source, annotations));
return null; return null;
} }
private Aggregate createAggregate(int aggregateIndex, @Nullable Object source, Annotation[] annotations) { private Aggregate createAggregate(int aggregateIndex, @Nullable Object source, @Nullable Annotation[] annotations) {
List<Annotation> aggregateAnnotations = getAggregateAnnotations(annotations); List<Annotation> aggregateAnnotations = getAggregateAnnotations(annotations);
return new Aggregate(aggregateIndex, source, aggregateAnnotations); return new Aggregate(aggregateIndex, source, aggregateAnnotations);
} }
private List<Annotation> getAggregateAnnotations(Annotation[] annotations) { private List<Annotation> getAggregateAnnotations(@Nullable Annotation[] annotations) {
List<Annotation> result = new ArrayList<>(annotations.length); List<Annotation> result = new ArrayList<>(annotations.length);
addAggregateAnnotations(result, annotations); addAggregateAnnotations(result, annotations);
return result; return result;
} }
private void addAggregateAnnotations(List<Annotation> aggregateAnnotations, Annotation[] annotations) { private void addAggregateAnnotations(List<Annotation> aggregateAnnotations, @Nullable Annotation[] annotations) {
for (Annotation annotation : annotations) { for (Annotation annotation : annotations) {
if (annotation != null && !annotationFilter.matches(annotation)) { if (annotation != null && !annotationFilter.matches(annotation)) {
Annotation[] repeatedAnnotations = repeatableContainers.findRepeatedAnnotations(annotation); Annotation[] repeatedAnnotations = repeatableContainers.findRepeatedAnnotations(annotation);
@ -482,7 +483,7 @@ final class TypeMappedAnnotations implements MergedAnnotations {
} }
@Override @Override
public List<Aggregate> finish(@Nullable List<Aggregate> processResult) { public @NonNull List<Aggregate> finish(@Nullable List<Aggregate> processResult) {
return this.aggregates; return this.aggregates;
} }
} }

6
spring-core/src/main/java/org/springframework/core/io/support/SpringFactoriesLoader.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2023 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -584,11 +584,11 @@ public class SpringFactoriesLoader {
* @param function the resolver function * @param function the resolver function
* @return a new {@link ArgumentResolver} instance backed by the function * @return a new {@link ArgumentResolver} instance backed by the function
*/ */
static ArgumentResolver from(Function<Class<?>, Object> function) { static ArgumentResolver from(Function<Class<?>, @Nullable Object> function) {
return new ArgumentResolver() { return new ArgumentResolver() {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public <T> T resolve(Class<T> type) { public <T> @Nullable T resolve(Class<T> type) {
return (T) function.apply(type); return (T) function.apply(type);
} }
}; };

12
spring-core/src/main/java/org/springframework/core/type/AnnotatedTypeMetadata.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2023 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -87,7 +87,7 @@ public interface AnnotatedTypeMetadata {
* as map key (for example, "location") and the attribute's value as map value; or * as map key (for example, "location") and the attribute's value as map value; or
* {@code null} if no matching annotation is found * {@code null} if no matching annotation is found
*/ */
default @Nullable Map<String, Object> getAnnotationAttributes(String annotationName) { default @Nullable Map<String, @Nullable Object> getAnnotationAttributes(String annotationName) {
return getAnnotationAttributes(annotationName, false); return getAnnotationAttributes(annotationName, false);
} }
@ -106,7 +106,7 @@ public interface AnnotatedTypeMetadata {
* as map key (for example, "location") and the attribute's value as map value; or * as map key (for example, "location") and the attribute's value as map value; or
* {@code null} if no matching annotation is found * {@code null} if no matching annotation is found
*/ */
default @Nullable Map<String, Object> getAnnotationAttributes(String annotationName, default @Nullable Map<String, @Nullable Object> getAnnotationAttributes(String annotationName,
boolean classValuesAsString) { boolean classValuesAsString) {
MergedAnnotation<Annotation> annotation = getAnnotations().get(annotationName, MergedAnnotation<Annotation> annotation = getAnnotations().get(annotationName,
@ -129,7 +129,7 @@ public interface AnnotatedTypeMetadata {
* map value; or {@code null} if no matching annotation is found * map value; or {@code null} if no matching annotation is found
* @see #getAllAnnotationAttributes(String, boolean) * @see #getAllAnnotationAttributes(String, boolean)
*/ */
default @Nullable MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName) { default @Nullable MultiValueMap<String, @Nullable Object> getAllAnnotationAttributes(String annotationName) {
return getAllAnnotationAttributes(annotationName, false); return getAllAnnotationAttributes(annotationName, false);
} }
@ -148,7 +148,7 @@ public interface AnnotatedTypeMetadata {
* map value; or {@code null} if no matching annotation is found * map value; or {@code null} if no matching annotation is found
* @see #getAllAnnotationAttributes(String) * @see #getAllAnnotationAttributes(String)
*/ */
default @Nullable MultiValueMap<String, Object> getAllAnnotationAttributes( default @Nullable MultiValueMap<String, @Nullable Object> getAllAnnotationAttributes(
String annotationName, boolean classValuesAsString) { String annotationName, boolean classValuesAsString) {
Adapt[] adaptations = Adapt.values(classValuesAsString, true); Adapt[] adaptations = Adapt.values(classValuesAsString, true);
@ -156,7 +156,7 @@ public interface AnnotatedTypeMetadata {
.filter(MergedAnnotationPredicates.unique(MergedAnnotation::getMetaTypes)) .filter(MergedAnnotationPredicates.unique(MergedAnnotation::getMetaTypes))
.map(MergedAnnotation::withNonMergedAttributes) .map(MergedAnnotation::withNonMergedAttributes)
.collect(MergedAnnotationCollectors.toMultiValueMap( .collect(MergedAnnotationCollectors.toMultiValueMap(
map -> (map.isEmpty() ? null : map), adaptations)); (MultiValueMap<String, @Nullable Object> map) -> (map.isEmpty() ? null : map), adaptations));
} }
/** /**

7
spring-core/src/main/java/org/springframework/core/type/StandardAnnotationMetadata.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2021 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -105,7 +105,7 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements
} }
@Override @Override
public @Nullable Map<String, Object> getAnnotationAttributes(String annotationName, boolean classValuesAsString) { public @Nullable Map<String, @Nullable Object> getAnnotationAttributes(String annotationName, boolean classValuesAsString) {
if (this.nestedAnnotationsAsMap) { if (this.nestedAnnotationsAsMap) {
return AnnotationMetadata.super.getAnnotationAttributes(annotationName, classValuesAsString); return AnnotationMetadata.super.getAnnotationAttributes(annotationName, classValuesAsString);
} }
@ -114,7 +114,8 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements
} }
@Override @Override
public @Nullable MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName, boolean classValuesAsString) { @SuppressWarnings("NullAway") // Null-safety of Java super method not yet managed
public @Nullable MultiValueMap<String, @Nullable Object> getAllAnnotationAttributes(String annotationName, boolean classValuesAsString) {
if (this.nestedAnnotationsAsMap) { if (this.nestedAnnotationsAsMap) {
return AnnotationMetadata.super.getAllAnnotationAttributes(annotationName, classValuesAsString); return AnnotationMetadata.super.getAllAnnotationAttributes(annotationName, classValuesAsString);
} }

7
spring-core/src/main/java/org/springframework/core/type/StandardMethodMetadata.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2023 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -131,7 +131,7 @@ public class StandardMethodMetadata implements MethodMetadata {
} }
@Override @Override
public @Nullable Map<String, Object> getAnnotationAttributes(String annotationName, boolean classValuesAsString) { public @Nullable Map<String, @Nullable Object> getAnnotationAttributes(String annotationName, boolean classValuesAsString) {
if (this.nestedAnnotationsAsMap) { if (this.nestedAnnotationsAsMap) {
return MethodMetadata.super.getAnnotationAttributes(annotationName, classValuesAsString); return MethodMetadata.super.getAnnotationAttributes(annotationName, classValuesAsString);
} }
@ -140,7 +140,8 @@ public class StandardMethodMetadata implements MethodMetadata {
} }
@Override @Override
public @Nullable MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName, boolean classValuesAsString) { @SuppressWarnings("NullAway") // Null-safety of Java super method not yet managed
public @Nullable MultiValueMap<String, @Nullable Object> getAllAnnotationAttributes(String annotationName, boolean classValuesAsString) {
if (this.nestedAnnotationsAsMap) { if (this.nestedAnnotationsAsMap) {
return MethodMetadata.super.getAllAnnotationAttributes(annotationName, classValuesAsString); return MethodMetadata.super.getAllAnnotationAttributes(annotationName, classValuesAsString);
} }

6
spring-core/src/main/java/org/springframework/util/CollectionUtils.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2024 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -64,7 +64,7 @@ public abstract class CollectionUtils {
* @return whether the given Collection is empty * @return whether the given Collection is empty
*/ */
@Contract("null -> true") @Contract("null -> true")
public static boolean isEmpty(@Nullable Collection<?> collection) { public static boolean isEmpty(@Nullable Collection<? extends @Nullable Object> collection) {
return (collection == null || collection.isEmpty()); return (collection == null || collection.isEmpty());
} }
@ -75,7 +75,7 @@ public abstract class CollectionUtils {
* @return whether the given Map is empty * @return whether the given Map is empty
*/ */
@Contract("null -> true") @Contract("null -> true")
public static boolean isEmpty(@Nullable Map<?, ?> map) { public static boolean isEmpty(@Nullable Map<?, ? extends @Nullable Object> map) {
return (map == null || map.isEmpty()); return (map == null || map.isEmpty());
} }

10
spring-core/src/main/java/org/springframework/util/CompositeCollection.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2024 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -20,6 +20,8 @@ import java.lang.reflect.Array;
import java.util.Collection; import java.util.Collection;
import java.util.Iterator; import java.util.Iterator;
import org.jspecify.annotations.Nullable;
/** /**
* Composite collection that combines two other collections. This type is only * Composite collection that combines two other collections. This type is only
@ -83,10 +85,10 @@ class CompositeCollection<E> implements Collection<E> {
} }
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings({"unchecked","NullAway"}) // Overridden method does not define nullness
public <T> T[] toArray(T[] a) { public <T> @Nullable T[] toArray(@Nullable T[] a) {
int size = this.size(); int size = this.size();
T[] result; @Nullable T[] result;
if (a.length >= size) { if (a.length >= size) {
result = a; result = a;
} }

12
spring-core/src/main/java/org/springframework/util/ConcurrentReferenceHashMap.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2024 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -456,7 +456,7 @@ public class ConcurrentReferenceHashMap<K, V> extends AbstractMap<K, V> implemen
* Array of references indexed using the low order bits from the hash. * Array of references indexed using the low order bits from the hash.
* This property should only be set along with {@code resizeThreshold}. * This property should only be set along with {@code resizeThreshold}.
*/ */
private volatile Reference<K, V>[] references; private volatile @Nullable Reference<K, V>[] references;
/** /**
* The total number of references contained in this segment. This includes chained * The total number of references contained in this segment. This includes chained
@ -485,7 +485,7 @@ public class ConcurrentReferenceHashMap<K, V> extends AbstractMap<K, V> implemen
return null; return null;
} }
// Use a local copy to protect against other threads writing // Use a local copy to protect against other threads writing
Reference<K, V>[] references = this.references; @Nullable Reference<K, V>[] references = this.references;
int index = getIndex(hash, references); int index = getIndex(hash, references);
Reference<K, V> head = references[index]; Reference<K, V> head = references[index];
return findInChain(head, key, hash); return findInChain(head, key, hash);
@ -641,7 +641,7 @@ public class ConcurrentReferenceHashMap<K, V> extends AbstractMap<K, V> implemen
} }
} }
private @Nullable Reference<K, V> findInChain(Reference<K, V> ref, @Nullable Object key, int hash) { private @Nullable Reference<K, V> findInChain(@Nullable Reference<K, V> ref, @Nullable Object key, int hash) {
Reference<K, V> currRef = ref; Reference<K, V> currRef = ref;
while (currRef != null) { while (currRef != null) {
if (currRef.getHash() == hash) { if (currRef.getHash() == hash) {
@ -663,7 +663,7 @@ public class ConcurrentReferenceHashMap<K, V> extends AbstractMap<K, V> implemen
return new Reference[size]; return new Reference[size];
} }
private int getIndex(int hash, Reference<K, V>[] references) { private int getIndex(int hash, @Nullable Reference<K, V>[] references) {
return (hash & (references.length - 1)); return (hash & (references.length - 1));
} }
@ -879,7 +879,7 @@ public class ConcurrentReferenceHashMap<K, V> extends AbstractMap<K, V> implemen
private int referenceIndex; private int referenceIndex;
private Reference<K, V> @Nullable [] references; private @Nullable Reference<K, V> @Nullable [] references;
private @Nullable Reference<K, V> reference; private @Nullable Reference<K, V> reference;

12
spring-core/src/main/java/org/springframework/util/MethodInvoker.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2024 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -49,7 +49,7 @@ public class MethodInvoker {
private @Nullable String staticMethod; private @Nullable String staticMethod;
private Object @Nullable [] arguments; private @Nullable Object @Nullable [] arguments;
/** The method we will call. */ /** The method we will call. */
private @Nullable Method methodObject; private @Nullable Method methodObject;
@ -127,14 +127,14 @@ public class MethodInvoker {
* Set arguments for the method invocation. If this property is not set, * Set arguments for the method invocation. If this property is not set,
* or the Object array is of length 0, a method with no arguments is assumed. * or the Object array is of length 0, a method with no arguments is assumed.
*/ */
public void setArguments(Object @Nullable ... arguments) { public void setArguments(@Nullable Object... arguments) {
this.arguments = arguments; this.arguments = arguments;
} }
/** /**
* Return the arguments for the method invocation. * Return the arguments for the method invocation.
*/ */
public Object[] getArguments() { public @Nullable Object[] getArguments() {
return (this.arguments != null ? this.arguments : EMPTY_ARGUMENTS); return (this.arguments != null ? this.arguments : EMPTY_ARGUMENTS);
} }
@ -166,7 +166,7 @@ public class MethodInvoker {
Assert.notNull(targetClass, "Either 'targetClass' or 'targetObject' is required"); Assert.notNull(targetClass, "Either 'targetClass' or 'targetObject' is required");
Assert.notNull(targetMethod, "Property 'targetMethod' is required"); Assert.notNull(targetMethod, "Property 'targetMethod' is required");
Object[] arguments = getArguments(); @Nullable Object[] arguments = getArguments();
Class<?>[] argTypes = new Class<?>[arguments.length]; Class<?>[] argTypes = new Class<?>[arguments.length];
for (int i = 0; i < arguments.length; ++i) { for (int i = 0; i < arguments.length; ++i) {
argTypes[i] = (arguments[i] != null ? arguments[i].getClass() : Object.class); argTypes[i] = (arguments[i] != null ? arguments[i].getClass() : Object.class);
@ -206,7 +206,7 @@ public class MethodInvoker {
*/ */
protected @Nullable Method findMatchingMethod() { protected @Nullable Method findMatchingMethod() {
String targetMethod = getTargetMethod(); String targetMethod = getTargetMethod();
Object[] arguments = getArguments(); @Nullable Object[] arguments = getArguments();
int argCount = arguments.length; int argCount = arguments.length;
Class<?> targetClass = getTargetClass(); Class<?> targetClass = getTargetClass();

4
spring-core/src/main/java/org/springframework/util/MultiValueMap.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2024 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -29,7 +29,7 @@ import org.jspecify.annotations.Nullable;
* @param <K> the key type * @param <K> the key type
* @param <V> the value element type * @param <V> the value element type
*/ */
public interface MultiValueMap<K, V> extends Map<K, List<V>> { public interface MultiValueMap<K, V extends @Nullable Object> extends Map<K, List<V>> {
/** /**
* Return the first value for the given key. * Return the first value for the given key.

10
spring-core/src/main/java/org/springframework/util/ObjectUtils.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2024 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -188,7 +188,7 @@ public abstract class ObjectUtils {
* @param element the element to check for * @param element the element to check for
* @return whether the element has been found in the given array * @return whether the element has been found in the given array
*/ */
public static boolean containsElement(Object @Nullable [] array, Object element) { public static boolean containsElement(@Nullable Object @Nullable [] array, @Nullable Object element) {
if (array == null) { if (array == null) {
return false; return false;
} }
@ -266,7 +266,7 @@ public abstract class ObjectUtils {
* @return the new array (of the same component type; never {@code null}) * @return the new array (of the same component type; never {@code null})
* @since 6.0 * @since 6.0
*/ */
public static <A, O extends A> A[] addObjectToArray(A @Nullable [] array, @Nullable O obj, int position) { public static <A, O extends A> @Nullable A[] addObjectToArray(A @Nullable [] array, @Nullable O obj, int position) {
Class<?> componentType = Object.class; Class<?> componentType = Object.class;
if (array != null) { if (array != null) {
componentType = array.getClass().componentType(); componentType = array.getClass().componentType();
@ -276,7 +276,7 @@ public abstract class ObjectUtils {
} }
int newArrayLength = (array != null ? array.length + 1 : 1); int newArrayLength = (array != null ? array.length + 1 : 1);
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
A[] newArray = (A[]) Array.newInstance(componentType, newArrayLength); @Nullable A[] newArray = (A[]) Array.newInstance(componentType, newArrayLength);
if (array != null) { if (array != null) {
System.arraycopy(array, 0, newArray, 0, position); System.arraycopy(array, 0, newArray, 0, position);
System.arraycopy(array, position, newArray, position + 1, array.length - position); System.arraycopy(array, position, newArray, position + 1, array.length - position);
@ -651,7 +651,7 @@ public abstract class ObjectUtils {
* @param array the array to build a String representation for * @param array the array to build a String representation for
* @return a String representation of {@code array} * @return a String representation of {@code array}
*/ */
public static String nullSafeToString(Object @Nullable [] array) { public static String nullSafeToString(@Nullable Object @Nullable [] array) {
if (array == null) { if (array == null) {
return NULL_STRING; return NULL_STRING;
} }

6
spring-core/src/main/java/org/springframework/util/StringUtils.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2024 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -1038,12 +1038,12 @@ public abstract class StringUtils {
* @param array the original {@code String} array (potentially empty) * @param array the original {@code String} array (potentially empty)
* @return the resulting array (of the same size) with trimmed elements * @return the resulting array (of the same size) with trimmed elements
*/ */
public static String[] trimArrayElements(String[] array) { public static @Nullable String[] trimArrayElements(@Nullable String[] array) {
if (ObjectUtils.isEmpty(array)) { if (ObjectUtils.isEmpty(array)) {
return array; return array;
} }
String[] result = new String[array.length]; @Nullable String[] result = new String[array.length];
for (int i = 0; i < array.length; i++) { for (int i = 0; i < array.length; i++) {
String element = array[i]; String element = array[i];
result[i] = (element != null ? element.trim() : null); result[i] = (element != null ? element.trim() : null);

21
spring-core/src/main/java/org/springframework/util/function/SingletonSupplier.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2023 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -22,6 +22,7 @@ import java.util.function.Supplier;
import org.jspecify.annotations.Nullable; import org.jspecify.annotations.Nullable;
import org.springframework.lang.Contract;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**
@ -38,11 +39,11 @@ import org.springframework.util.Assert;
* @since 5.1 * @since 5.1
* @param <T> the type of results supplied by this supplier * @param <T> the type of results supplied by this supplier
*/ */
public class SingletonSupplier<T> implements Supplier<T> { public class SingletonSupplier<T> implements Supplier<@Nullable T> {
private final @Nullable Supplier<? extends T> instanceSupplier; private final @Nullable Supplier<? extends @Nullable T> instanceSupplier;
private final @Nullable Supplier<? extends T> defaultSupplier; private final @Nullable Supplier<? extends @Nullable T> defaultSupplier;
private volatile @Nullable T singletonInstance; private volatile @Nullable T singletonInstance;
@ -58,7 +59,7 @@ public class SingletonSupplier<T> implements Supplier<T> {
* @param instance the singleton instance (potentially {@code null}) * @param instance the singleton instance (potentially {@code null})
* @param defaultSupplier the default supplier as a fallback * @param defaultSupplier the default supplier as a fallback
*/ */
public SingletonSupplier(@Nullable T instance, Supplier<? extends T> defaultSupplier) { public SingletonSupplier(@Nullable T instance, Supplier<? extends @Nullable T> defaultSupplier) {
this.instanceSupplier = null; this.instanceSupplier = null;
this.defaultSupplier = defaultSupplier; this.defaultSupplier = defaultSupplier;
this.singletonInstance = instance; this.singletonInstance = instance;
@ -70,17 +71,17 @@ public class SingletonSupplier<T> implements Supplier<T> {
* @param instanceSupplier the immediate instance supplier * @param instanceSupplier the immediate instance supplier
* @param defaultSupplier the default supplier as a fallback * @param defaultSupplier the default supplier as a fallback
*/ */
public SingletonSupplier(@Nullable Supplier<? extends T> instanceSupplier, Supplier<? extends T> defaultSupplier) { public SingletonSupplier(@Nullable Supplier<? extends @Nullable T> instanceSupplier, Supplier<? extends @Nullable T> defaultSupplier) {
this.instanceSupplier = instanceSupplier; this.instanceSupplier = instanceSupplier;
this.defaultSupplier = defaultSupplier; this.defaultSupplier = defaultSupplier;
} }
private SingletonSupplier(Supplier<? extends T> supplier) { private SingletonSupplier(Supplier<? extends @Nullable T> supplier) {
this.instanceSupplier = supplier; this.instanceSupplier = supplier;
this.defaultSupplier = null; this.defaultSupplier = null;
} }
private SingletonSupplier(T singletonInstance) { private SingletonSupplier(@Nullable T singletonInstance) {
this.instanceSupplier = null; this.instanceSupplier = null;
this.defaultSupplier = null; this.defaultSupplier = null;
this.singletonInstance = singletonInstance; this.singletonInstance = singletonInstance;
@ -141,6 +142,7 @@ public class SingletonSupplier<T> implements Supplier<T> {
* @param instance the singleton instance (potentially {@code null}) * @param instance the singleton instance (potentially {@code null})
* @return the singleton supplier, or {@code null} if the instance was {@code null} * @return the singleton supplier, or {@code null} if the instance was {@code null}
*/ */
@Contract("null -> null; !null -> !null")
public static <T> @Nullable SingletonSupplier<T> ofNullable(@Nullable T instance) { public static <T> @Nullable SingletonSupplier<T> ofNullable(@Nullable T instance) {
return (instance != null ? new SingletonSupplier<>(instance) : null); return (instance != null ? new SingletonSupplier<>(instance) : null);
} }
@ -159,7 +161,8 @@ public class SingletonSupplier<T> implements Supplier<T> {
* @param supplier the instance supplier (potentially {@code null}) * @param supplier the instance supplier (potentially {@code null})
* @return the singleton supplier, or {@code null} if the instance supplier was {@code null} * @return the singleton supplier, or {@code null} if the instance supplier was {@code null}
*/ */
public static <T> @Nullable SingletonSupplier<T> ofNullable(@Nullable Supplier<T> supplier) { @Contract("null -> null; !null -> !null")
public static <T> @Nullable SingletonSupplier<T> ofNullable(@Nullable Supplier<? extends @Nullable T> supplier) {
return (supplier != null ? new SingletonSupplier<>(supplier) : null); return (supplier != null ? new SingletonSupplier<>(supplier) : null);
} }

5
spring-core/src/main/java/org/springframework/util/function/SupplierUtils.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2024 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -20,6 +20,8 @@ import java.util.function.Supplier;
import org.jspecify.annotations.Nullable; import org.jspecify.annotations.Nullable;
import org.springframework.lang.Contract;
/** /**
* Convenience utilities for {@link java.util.function.Supplier} handling. * Convenience utilities for {@link java.util.function.Supplier} handling.
* *
@ -35,6 +37,7 @@ public abstract class SupplierUtils {
* @param supplier the supplier to resolve * @param supplier the supplier to resolve
* @return the supplier's result, or {@code null} if none * @return the supplier's result, or {@code null} if none
*/ */
@Contract("null -> null; !null -> !null")
public static <T> @Nullable T resolve(@Nullable Supplier<T> supplier) { public static <T> @Nullable T resolve(@Nullable Supplier<T> supplier) {
return (supplier != null ? supplier.get() : null); return (supplier != null ? supplier.get() : null);
} }

15
spring-core/src/test/java/org/springframework/core/annotation/AnnotationsScannerTests.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2023 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -29,6 +29,7 @@ import java.util.Objects;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable; import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -469,13 +470,13 @@ class AnnotationsScannerTests {
new AnnotationsProcessor<Object, String>() { new AnnotationsProcessor<Object, String>() {
@Override @Override
public String doWithAggregate(Object context, int aggregateIndex) { public @NonNull String doWithAggregate(Object context, int aggregateIndex) {
return ""; return "";
} }
@Override @Override
public String doWithAnnotations(Object context, int aggregateIndex, public @NonNull String doWithAnnotations(Object context, int aggregateIndex,
@Nullable Object source, Annotation[] annotations) { @Nullable Object source, @Nullable Annotation @Nullable [] annotations) {
throw new IllegalStateException("Should not call"); throw new IllegalStateException("Should not call");
} }
@ -501,13 +502,13 @@ class AnnotationsScannerTests {
new AnnotationsProcessor<Object, String>() { new AnnotationsProcessor<Object, String>() {
@Override @Override
public String doWithAnnotations(Object context, int aggregateIndex, public @NonNull String doWithAnnotations(Object context, int aggregateIndex,
@Nullable Object source, Annotation[] annotations) { @Nullable Object source, @Nullable Annotation @Nullable [] annotations) {
return "K"; return "K";
} }
@Override @Override
public String finish(@Nullable String result) { public @NonNull String finish(@Nullable String result) {
return "O" + result; return "O" + result;
} }

Loading…
Cancel
Save