Fix container detection in RepeatableContainers.standardRepeatables()

StandardRepeatableContainers.computeRepeatedAnnotationsMethod()
previously treated any annotation as a container annotation if it
merely declared a `value` attribute returning an array of a
@⁠Repeatable annotated type, without verifying that the repeatable
annotation actually references the candidate container via
@⁠Repeatable.value().

Consequently, an unrelated annotation that happened to have the same
shape as the official container (e.g. a "fake" container with a `value`
attribute returning the same repeatable type) was incorrectly
recognized as a container.

This commit aligns the detection logic with the Java language semantics
by additionally asserting that the container type declared in
@⁠Repeatable.value() is identical to the candidate annotation type.

Closes gh-37326

Signed-off-by: Chengang Guan <guanchengang@qq.com>
This commit is contained in:
Chengang Guan
2026-09-30 17:41:07 +02:00
committed by GitHub
parent 9289f71ad7
commit 5e7630469c
2 changed files with 23 additions and 3 deletions
@@ -274,9 +274,11 @@ public abstract class RepeatableContainers {
Class<?> returnType = method.getReturnType();
if (returnType.isArray()) {
Class<?> componentType = returnType.componentType();
if (Annotation.class.isAssignableFrom(componentType) &&
componentType.isAnnotationPresent(Repeatable.class)) {
return method;
if (Annotation.class.isAssignableFrom(componentType)) {
Repeatable repeatable = componentType.getAnnotation(Repeatable.class);
if (repeatable != null && repeatable.value() == annotationType) {
return method;
}
}
}
}
@@ -37,6 +37,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
*
* @author Phillip Webb
* @author Sam Brannen
* @author Chengang Guan
*/
class RepeatableContainersTests {
@@ -79,6 +80,13 @@ class RepeatableContainersTests {
assertThat(values).containsExactly("a", "b");
}
@Test
void standardRepeatablesWhenContainerNotReferencedByRepeatableReturnsNull() {
Object[] values = findRepeatedAnnotationValues(RepeatableContainers.standardRepeatables(),
FakeRepeatablesTestCase.class, FakeStandardContainer.class);
assertThat(values).isNull();
}
}
@Nested
@@ -211,6 +219,12 @@ class RepeatableContainersTests {
StandardRepeatable[] value();
}
@Retention(RetentionPolicy.RUNTIME)
@interface FakeStandardContainer {
StandardRepeatable[] value();
}
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(StandardContainer.class)
@interface StandardRepeatable {
@@ -263,4 +277,8 @@ class RepeatableContainersTests {
static class ExplicitRepeatablesTestCase {
}
@FakeStandardContainer({ @StandardRepeatable("a"), @StandardRepeatable("b") })
static class FakeRepeatablesTestCase {
}
}