Skip to content

Meta-annotations with attributes are still unnecessarily synthesized in MergedAnnotations #37135

Description

@sbrannen

Overview

While revisiting the synthesis logic in TypeMappedAnnotation, I discovered that meta-annotations that declare at least one attribute are still unnecessarily synthesized when synthesizing a MergedAnnotation retrieved via the MergedAnnotations API -- even though #28704 was supposed to have fixed this.

This is a side effect of the fix for #28716 (commit 622fc3e), in which I added the following check to TypeMappedAnnotation#isSynthesizable(Annotation).

if (getDistance() > 0 && this.resolvedMirrors.length > 0) {
    return true;
}

The intent was to force synthesis when an attribute's value needs to be resolved from a different level of the annotation hierarchy (the motivating case being a multi-level hierarchy whose ultimate root annotation declares none of its own attributes). However, resolvedMirrors is populated by AnnotationTypeMapping.MirrorSets#resolve(), which always returns an array sized according to the number of attributes declared by the mapped annotation type -- regardless of whether any of those attributes actually participate in mirroring or an @AliasFor override. As a result, resolvedMirrors.length > 0 is true for virtually any meta-annotation that declares at least one attribute, which reintroduces the unnecessary-synthesis behavior that #28704 fixed, just narrowed to meta-annotations that happen to declare attributes.

The following reproduces the regression.

@Retention(RetentionPolicy.RUNTIME)
@interface Meta {
	String value() default "meta";
}

@Meta
@Retention(RetentionPolicy.RUNTIME)
@interface Composed {
}

@Composed
class Example {
}
@Test
void test() {
	Meta meta = MergedAnnotations.from(Example.class).get(Meta.class).synthesize();
	// Meta declares no @AliasFor, and Composed does not override any of its
	// attributes, so AnnotationTypeMapping#isSynthesizable() correctly returns
	// false for this mapping -- yet the annotation is synthesized anyway.
	assertThat(AnnotationUtils.isSynthesizedAnnotation(meta)).isFalse(); // fails: is true
}

Although this is technically a regression, I'll only apply a fix to main (7.1) since the regression has existed for over 4 years without causing major issues for anyone.

Related Issues

Metadata

Metadata

Assignees

Labels

in: coreIssues in core modules (aop, beans, core, context, expression)type: regressionA bug that is also a regression

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions