Skip to content

Commit

Permalink
Detect deprecated element in generic types
Browse files Browse the repository at this point in the history
This commit updates Spring AOT to suppress a deprecation warning for
a generic type that has a deprecated element. Previously we only were
checking for the raw class.

Closes gh-32850
  • Loading branch information
snicoll committed May 21, 2024
1 parent 481d036 commit f26483d
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ private GeneratedMethod generateBeanDefinitionMethod(GenerationContext generatio
this.aotContributions.forEach(aotContribution -> aotContribution.applyTo(generationContext, codeGenerator));

CodeWarnings codeWarnings = new CodeWarnings();
codeWarnings.detectDeprecation(this.registeredBean.getBeanClass());
codeWarnings.detectDeprecation(this.registeredBean.getBeanType());
return generatedMethods.add("getBeanDefinition", method -> {
method.addJavadoc("Get the $L definition for '$L'.",
(this.registeredBean.isInnerBean() ? "inner-bean" : "bean"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@
import java.util.StringJoiner;
import java.util.stream.Stream;

import org.springframework.core.ResolvableType;
import org.springframework.javapoet.AnnotationSpec;
import org.springframework.javapoet.CodeBlock;
import org.springframework.javapoet.MethodSpec;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;

/**
* Helper class to register warnings that the compiler may trigger on
Expand Down Expand Up @@ -72,6 +74,26 @@ public CodeWarnings detectDeprecation(Stream<AnnotatedElement> elements) {
return this;
}

/**
* Detect the presence of {@link Deprecated} on the signature of the
* specified {@link ResolvableType}.
* @param resolvableType a type signature
* @return {@code this} instance
*/
public CodeWarnings detectDeprecation(ResolvableType resolvableType) {
if (ResolvableType.NONE.equals(resolvableType)) {
return this;
}
Class<?> type = ClassUtils.getUserClass(resolvableType.toClass());
detectDeprecation(type);
if (resolvableType.hasGenerics() && !resolvableType.hasUnresolvableGenerics()) {
for (ResolvableType generic : resolvableType.getGenerics()) {
detectDeprecation(generic);
}
}
return this;
}

/**
* Include {@link SuppressWarnings} on the specified method if necessary.
* @param method the method to update
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -782,6 +782,19 @@ void generateBeanDefinitionMethodWithDeprecatedTargetClass() {
compileAndCheckWarnings(method);
}

@Test
void generateBeanDefinitionMethodWithDeprecatedGenericElementInTargetClass() {
RootBeanDefinition beanDefinition = new RootBeanDefinition();
beanDefinition.setTargetType(ResolvableType.forClassWithGenerics(GenericBean.class, DeprecatedBean.class));
RegisteredBean registeredBean = registerBean(beanDefinition);
BeanDefinitionMethodGenerator generator = new BeanDefinitionMethodGenerator(
methodGeneratorFactory, registeredBean, null,
Collections.emptyList());
MethodReference method = generator.generateBeanDefinitionMethod(
generationContext, beanRegistrationsCode);
compileAndCheckWarnings(method);
}

private void compileAndCheckWarnings(MethodReference methodReference) {
assertThatNoException().isThrownBy(() -> compile(TEST_COMPILER, methodReference,
((instanceSupplier, compiled) -> {})));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,21 @@
package org.springframework.beans.factory.aot;

import java.util.function.Consumer;
import java.util.stream.Stream;

import javax.lang.model.element.Modifier;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

import org.springframework.aot.test.generate.TestGenerationContext;
import org.springframework.beans.testfixture.beans.GenericBean;
import org.springframework.beans.testfixture.beans.factory.aot.DeferredTypeBuilder;
import org.springframework.beans.testfixture.beans.factory.generator.deprecation.DeprecatedBean;
import org.springframework.beans.testfixture.beans.factory.generator.deprecation.DeprecatedForRemovalBean;
import org.springframework.core.ResolvableType;
import org.springframework.core.test.tools.Compiled;
import org.springframework.core.test.tools.TestCompiler;
import org.springframework.javapoet.MethodSpec;
Expand Down Expand Up @@ -98,6 +104,40 @@ void detectDeprecationOnAnnotatedElementWithDeprecatedForRemoval() {
assertThat(this.codeWarnings.getWarnings()).containsExactly("removal");
}

@ParameterizedTest
@MethodSource("resolvableTypesWithDeprecated")
void detectDeprecationOnResolvableTypeWithDeprecated(ResolvableType resolvableType) {
this.codeWarnings.detectDeprecation(resolvableType);
assertThat(this.codeWarnings.getWarnings()).containsExactly("deprecation");
}

@SuppressWarnings("deprecation")
static Stream<Arguments> resolvableTypesWithDeprecated() {
return Stream.of(
Arguments.of(ResolvableType.forClass(DeprecatedBean.class)),
Arguments.of(ResolvableType.forClassWithGenerics(GenericBean.class, DeprecatedBean.class)),
Arguments.of(ResolvableType.forClassWithGenerics(GenericBean.class,
ResolvableType.forClassWithGenerics(GenericBean.class, DeprecatedBean.class)))
);
}

@ParameterizedTest
@MethodSource("resolvableTypesWithDeprecatedForRemoval")
void detectDeprecationOnResolvableTypeWithDeprecatedForRemoval(ResolvableType resolvableType) {
this.codeWarnings.detectDeprecation(resolvableType);
assertThat(this.codeWarnings.getWarnings()).containsExactly("removal");
}

@SuppressWarnings("removal")
static Stream<Arguments> resolvableTypesWithDeprecatedForRemoval() {
return Stream.of(
Arguments.of(ResolvableType.forClass(DeprecatedForRemovalBean.class)),
Arguments.of(ResolvableType.forClassWithGenerics(GenericBean.class, DeprecatedForRemovalBean.class)),
Arguments.of(ResolvableType.forClassWithGenerics(GenericBean.class,
ResolvableType.forClassWithGenerics(GenericBean.class, DeprecatedForRemovalBean.class)))
);
}

@Test
void toStringIncludeWarnings() {
this.codeWarnings.register("deprecation");
Expand Down

0 comments on commit f26483d

Please sign in to comment.