From f26483d272d07f6f0baf5ae0a0cfbe0e99597807 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Tue, 21 May 2024 08:53:18 +0200 Subject: [PATCH] Detect deprecated element in generic types 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 --- .../aot/BeanDefinitionMethodGenerator.java | 2 +- .../beans/factory/aot/CodeWarnings.java | 22 ++++++++++ .../BeanDefinitionMethodGeneratorTests.java | 13 ++++++ .../beans/factory/aot/CodeWarningsTests.java | 40 +++++++++++++++++++ 4 files changed, 76 insertions(+), 1 deletion(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionMethodGenerator.java b/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionMethodGenerator.java index 01c2ab71a7..074e87df87 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionMethodGenerator.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionMethodGenerator.java @@ -165,7 +165,7 @@ class BeanDefinitionMethodGenerator { 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"), diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/aot/CodeWarnings.java b/spring-beans/src/main/java/org/springframework/beans/factory/aot/CodeWarnings.java index ba0de5056e..fd2a42c7c1 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/aot/CodeWarnings.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/aot/CodeWarnings.java @@ -23,10 +23,12 @@ import java.util.Set; 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 @@ -72,6 +74,26 @@ class CodeWarnings { 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 diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionMethodGeneratorTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionMethodGeneratorTests.java index af9bf3793e..e7c986925a 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionMethodGeneratorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionMethodGeneratorTests.java @@ -782,6 +782,19 @@ class BeanDefinitionMethodGeneratorTests { 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) -> {}))); diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/aot/CodeWarningsTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/aot/CodeWarningsTests.java index 4f7c57edcd..fdc8a9af73 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/aot/CodeWarningsTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/aot/CodeWarningsTests.java @@ -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; @@ -98,6 +104,40 @@ class CodeWarningsTests { 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 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 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");