Detect use of deprecated API
This commit is a best effort attempt at identifying the members that code generation invokes and might be deprecated. It introduces a CodeWarnings helper class that records warnings, with special handling for `@Deprecated`. See gh-29597
This commit is contained in:
parent
35372e5e72
commit
ea66883d79
|
|
@ -164,11 +164,14 @@ class BeanDefinitionMethodGenerator {
|
||||||
|
|
||||||
this.aotContributions.forEach(aotContribution -> aotContribution.applyTo(generationContext, codeGenerator));
|
this.aotContributions.forEach(aotContribution -> aotContribution.applyTo(generationContext, codeGenerator));
|
||||||
|
|
||||||
|
CodeWarnings codeWarnings = new CodeWarnings();
|
||||||
|
codeWarnings.detectDeprecation(this.registeredBean.getBeanClass());
|
||||||
return generatedMethods.add("getBeanDefinition", method -> {
|
return generatedMethods.add("getBeanDefinition", method -> {
|
||||||
method.addJavadoc("Get the $L definition for '$L'.",
|
method.addJavadoc("Get the $L definition for '$L'.",
|
||||||
(this.registeredBean.isInnerBean() ? "inner-bean" : "bean"),
|
(this.registeredBean.isInnerBean() ? "inner-bean" : "bean"),
|
||||||
getName());
|
getName());
|
||||||
method.addModifiers(modifier, Modifier.STATIC);
|
method.addModifiers(modifier, Modifier.STATIC);
|
||||||
|
codeWarnings.suppress(method);
|
||||||
method.returns(BeanDefinition.class);
|
method.returns(BeanDefinition.class);
|
||||||
method.addCode(codeGenerator.generateCode(generationContext));
|
method.addCode(codeGenerator.generateCode(generationContext));
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,125 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2023 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.beans.factory.aot;
|
||||||
|
|
||||||
|
import java.lang.reflect.AnnotatedElement;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.StringJoiner;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.springframework.javapoet.AnnotationSpec;
|
||||||
|
import org.springframework.javapoet.CodeBlock;
|
||||||
|
import org.springframework.javapoet.MethodSpec;
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class to register warnings that the compiler may trigger on
|
||||||
|
* generated code.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
* @see SuppressWarnings
|
||||||
|
*/
|
||||||
|
class CodeWarnings {
|
||||||
|
|
||||||
|
private final Set<String> warnings = new LinkedHashSet<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a warning to be included for this block. Does nothing if
|
||||||
|
* the warning is already registered.
|
||||||
|
* @param warning the warning to register, if it hasn't been already
|
||||||
|
*/
|
||||||
|
public void register(String warning) {
|
||||||
|
this.warnings.add(warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect the presence of {@link Deprecated} on the specified elements.
|
||||||
|
* @param elements the elements to check
|
||||||
|
* @return {@code this} instance
|
||||||
|
*/
|
||||||
|
public CodeWarnings detectDeprecation(AnnotatedElement... elements) {
|
||||||
|
for (AnnotatedElement element : elements) {
|
||||||
|
register(element.getAnnotation(Deprecated.class));
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect the presence of {@link Deprecated} on the specified elements.
|
||||||
|
* @param elements the elements to check
|
||||||
|
* @return {@code this} instance
|
||||||
|
*/
|
||||||
|
public CodeWarnings detectDeprecation(Stream<AnnotatedElement> elements) {
|
||||||
|
elements.forEach(element -> register(element.getAnnotation(Deprecated.class)));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Include a {@link SuppressWarnings} on the specified method if necessary.
|
||||||
|
* @param method the method to update
|
||||||
|
*/
|
||||||
|
public void suppress(MethodSpec.Builder method) {
|
||||||
|
if (this.warnings.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
method.addAnnotation(buildAnnotationSpec());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the currently registered warnings.
|
||||||
|
* @return the warnings
|
||||||
|
*/
|
||||||
|
protected Set<String> getWarnings() {
|
||||||
|
return Collections.unmodifiableSet(this.warnings);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void register(@Nullable Deprecated annotation) {
|
||||||
|
if (annotation != null) {
|
||||||
|
if (annotation.forRemoval()) {
|
||||||
|
register("removal");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
register("deprecation");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private AnnotationSpec buildAnnotationSpec() {
|
||||||
|
return AnnotationSpec.builder(SuppressWarnings.class)
|
||||||
|
.addMember("value", generateValueCode()).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private CodeBlock generateValueCode() {
|
||||||
|
if (this.warnings.size() == 1) {
|
||||||
|
return CodeBlock.of("$S", this.warnings.iterator().next());
|
||||||
|
}
|
||||||
|
CodeBlock values = CodeBlock.join(this.warnings.stream()
|
||||||
|
.map(warning -> CodeBlock.of("$S", warning)).toList(), ", ");
|
||||||
|
return CodeBlock.of("{ $L }", values);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return new StringJoiner(", ", CodeWarnings.class.getSimpleName(), "")
|
||||||
|
.add(this.warnings.toString())
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -21,6 +21,7 @@ import java.lang.reflect.Executable;
|
||||||
import java.lang.reflect.Member;
|
import java.lang.reflect.Member;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.lang.reflect.Parameter;
|
||||||
import java.lang.reflect.Proxy;
|
import java.lang.reflect.Proxy;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
@ -189,11 +190,15 @@ public class InstanceSupplierCodeGenerator {
|
||||||
private CodeBlock generateCodeForInaccessibleConstructor(String beanName, Class<?> beanClass,
|
private CodeBlock generateCodeForInaccessibleConstructor(String beanName, Class<?> beanClass,
|
||||||
Constructor<?> constructor, boolean dependsOnBean, Consumer<ReflectionHints> hints) {
|
Constructor<?> constructor, boolean dependsOnBean, Consumer<ReflectionHints> hints) {
|
||||||
|
|
||||||
|
CodeWarnings codeWarnings = new CodeWarnings();
|
||||||
|
codeWarnings.detectDeprecation(beanClass, constructor)
|
||||||
|
.detectDeprecation(Arrays.stream(constructor.getParameters()).map(Parameter::getType));
|
||||||
hints.accept(this.generationContext.getRuntimeHints().reflection());
|
hints.accept(this.generationContext.getRuntimeHints().reflection());
|
||||||
|
|
||||||
GeneratedMethod generatedMethod = generateGetInstanceSupplierMethod(method -> {
|
GeneratedMethod generatedMethod = generateGetInstanceSupplierMethod(method -> {
|
||||||
method.addJavadoc("Get the bean instance supplier for '$L'.", beanName);
|
method.addJavadoc("Get the bean instance supplier for '$L'.", beanName);
|
||||||
method.addModifiers(PRIVATE_STATIC);
|
method.addModifiers(PRIVATE_STATIC);
|
||||||
|
codeWarnings.suppress(method);
|
||||||
method.returns(ParameterizedTypeName.get(BeanInstanceSupplier.class, beanClass));
|
method.returns(ParameterizedTypeName.get(BeanInstanceSupplier.class, beanClass));
|
||||||
int parameterOffset = (!dependsOnBean) ? 0 : 1;
|
int parameterOffset = (!dependsOnBean) ? 0 : 1;
|
||||||
method.addStatement(generateResolverForConstructor(beanClass, constructor, parameterOffset));
|
method.addStatement(generateResolverForConstructor(beanClass, constructor, parameterOffset));
|
||||||
|
|
@ -206,8 +211,12 @@ public class InstanceSupplierCodeGenerator {
|
||||||
String beanName, Class<?> beanClass, Constructor<?> constructor, Class<?> declaringClass,
|
String beanName, Class<?> beanClass, Constructor<?> constructor, Class<?> declaringClass,
|
||||||
boolean dependsOnBean, javax.lang.model.element.Modifier... modifiers) {
|
boolean dependsOnBean, javax.lang.model.element.Modifier... modifiers) {
|
||||||
|
|
||||||
|
CodeWarnings codeWarnings = new CodeWarnings();
|
||||||
|
codeWarnings.detectDeprecation(beanClass, constructor, declaringClass)
|
||||||
|
.detectDeprecation(Arrays.stream(constructor.getParameters()).map(Parameter::getType));
|
||||||
method.addJavadoc("Get the bean instance supplier for '$L'.", beanName);
|
method.addJavadoc("Get the bean instance supplier for '$L'.", beanName);
|
||||||
method.addModifiers(modifiers);
|
method.addModifiers(modifiers);
|
||||||
|
codeWarnings.suppress(method);
|
||||||
method.returns(ParameterizedTypeName.get(BeanInstanceSupplier.class, beanClass));
|
method.returns(ParameterizedTypeName.get(BeanInstanceSupplier.class, beanClass));
|
||||||
|
|
||||||
int parameterOffset = (!dependsOnBean) ? 0 : 1;
|
int parameterOffset = (!dependsOnBean) ? 0 : 1;
|
||||||
|
|
@ -300,9 +309,13 @@ public class InstanceSupplierCodeGenerator {
|
||||||
|
|
||||||
String factoryMethodName = factoryMethod.getName();
|
String factoryMethodName = factoryMethod.getName();
|
||||||
Class<?> suppliedType = ClassUtils.resolvePrimitiveIfNecessary(factoryMethod.getReturnType());
|
Class<?> suppliedType = ClassUtils.resolvePrimitiveIfNecessary(factoryMethod.getReturnType());
|
||||||
|
CodeWarnings codeWarnings = new CodeWarnings();
|
||||||
|
codeWarnings.detectDeprecation(declaringClass, factoryMethod, suppliedType)
|
||||||
|
.detectDeprecation(Arrays.stream(factoryMethod.getParameters()).map(Parameter::getType));
|
||||||
|
|
||||||
method.addJavadoc("Get the bean instance supplier for '$L'.", beanName);
|
method.addJavadoc("Get the bean instance supplier for '$L'.", beanName);
|
||||||
method.addModifiers(modifiers);
|
method.addModifiers(modifiers);
|
||||||
|
codeWarnings.suppress(method);
|
||||||
method.returns(ParameterizedTypeName.get(BeanInstanceSupplier.class, suppliedType));
|
method.returns(ParameterizedTypeName.get(BeanInstanceSupplier.class, suppliedType));
|
||||||
|
|
||||||
CodeBlock.Builder code = CodeBlock.builder();
|
CodeBlock.Builder code = CodeBlock.builder();
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ import java.util.function.Supplier;
|
||||||
import javax.lang.model.element.Modifier;
|
import javax.lang.model.element.Modifier;
|
||||||
import javax.xml.parsers.DocumentBuilderFactory;
|
import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Nested;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import org.springframework.aot.generate.GeneratedMethod;
|
import org.springframework.aot.generate.GeneratedMethod;
|
||||||
|
|
@ -52,6 +53,7 @@ import org.springframework.beans.testfixture.beans.factory.aot.TestHierarchy;
|
||||||
import org.springframework.beans.testfixture.beans.factory.aot.TestHierarchy.Implementation;
|
import org.springframework.beans.testfixture.beans.factory.aot.TestHierarchy.Implementation;
|
||||||
import org.springframework.beans.testfixture.beans.factory.aot.TestHierarchy.One;
|
import org.springframework.beans.testfixture.beans.factory.aot.TestHierarchy.One;
|
||||||
import org.springframework.beans.testfixture.beans.factory.aot.TestHierarchy.Two;
|
import org.springframework.beans.testfixture.beans.factory.aot.TestHierarchy.Two;
|
||||||
|
import org.springframework.beans.testfixture.beans.factory.generator.deprecation.DeprecatedBean;
|
||||||
import org.springframework.core.ResolvableType;
|
import org.springframework.core.ResolvableType;
|
||||||
import org.springframework.core.test.io.support.MockSpringFactoriesLoader;
|
import org.springframework.core.test.io.support.MockSpringFactoriesLoader;
|
||||||
import org.springframework.core.test.tools.CompileWithForkedClassLoader;
|
import org.springframework.core.test.tools.CompileWithForkedClassLoader;
|
||||||
|
|
@ -66,6 +68,7 @@ import org.springframework.util.ReflectionUtils;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatNoException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link BeanDefinitionMethodGenerator} and
|
* Tests for {@link BeanDefinitionMethodGenerator} and
|
||||||
|
|
@ -740,6 +743,32 @@ class BeanDefinitionMethodGeneratorTests {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
class DeprecationTests {
|
||||||
|
|
||||||
|
private static final TestCompiler TEST_COMPILER = TestCompiler.forSystem()
|
||||||
|
.withCompilerOptions("-Xlint:all", "-Xlint:-rawtypes", "-Werror");
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void generateBeanDefinitionMethodWithDeprecatedTargetClass() {
|
||||||
|
RootBeanDefinition beanDefinition = new RootBeanDefinition(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) -> {})));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private void testBeanDefinitionMethodInCurrentFile(Class<?> targetType, RootBeanDefinition beanDefinition) {
|
private void testBeanDefinitionMethodInCurrentFile(Class<?> targetType, RootBeanDefinition beanDefinition) {
|
||||||
RegisteredBean registeredBean = registerBean(new RootBeanDefinition(beanDefinition));
|
RegisteredBean registeredBean = registerBean(new RootBeanDefinition(beanDefinition));
|
||||||
BeanDefinitionMethodGenerator generator = new BeanDefinitionMethodGenerator(
|
BeanDefinitionMethodGenerator generator = new BeanDefinitionMethodGenerator(
|
||||||
|
|
@ -764,6 +793,10 @@ class BeanDefinitionMethodGeneratorTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void compile(MethodReference method, BiConsumer<RootBeanDefinition, Compiled> result) {
|
private void compile(MethodReference method, BiConsumer<RootBeanDefinition, Compiled> result) {
|
||||||
|
compile(TestCompiler.forSystem(), method, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void compile(TestCompiler testCompiler, MethodReference method, BiConsumer<RootBeanDefinition, Compiled> result) {
|
||||||
this.beanRegistrationsCode.getTypeBuilder().set(type -> {
|
this.beanRegistrationsCode.getTypeBuilder().set(type -> {
|
||||||
CodeBlock methodInvocation = method.toInvokeCodeBlock(ArgumentCodeGenerator.none(),
|
CodeBlock methodInvocation = method.toInvokeCodeBlock(ArgumentCodeGenerator.none(),
|
||||||
this.beanRegistrationsCode.getClassName());
|
this.beanRegistrationsCode.getClassName());
|
||||||
|
|
@ -775,7 +808,7 @@ class BeanDefinitionMethodGeneratorTests {
|
||||||
.addCode("return $L;", methodInvocation).build());
|
.addCode("return $L;", methodInvocation).build());
|
||||||
});
|
});
|
||||||
this.generationContext.writeGeneratedContent();
|
this.generationContext.writeGeneratedContent();
|
||||||
TestCompiler.forSystem().with(this.generationContext).compile(compiled ->
|
testCompiler.with(this.generationContext).compile(compiled ->
|
||||||
result.accept((RootBeanDefinition) compiled.getInstance(Supplier.class).get(), compiled));
|
result.accept((RootBeanDefinition) compiled.getInstance(Supplier.class).get(), compiled));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,123 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2023 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.beans.factory.aot;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import javax.lang.model.element.Modifier;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.aot.test.generate.TestGenerationContext;
|
||||||
|
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.test.tools.Compiled;
|
||||||
|
import org.springframework.core.test.tools.TestCompiler;
|
||||||
|
import org.springframework.javapoet.MethodSpec;
|
||||||
|
import org.springframework.javapoet.MethodSpec.Builder;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link CodeWarnings}.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
*/
|
||||||
|
class CodeWarningsTests {
|
||||||
|
|
||||||
|
private static final TestCompiler TEST_COMPILER = TestCompiler.forSystem()
|
||||||
|
.withCompilerOptions("-Xlint:all", "-Werror");
|
||||||
|
|
||||||
|
private final CodeWarnings codeWarnings;
|
||||||
|
|
||||||
|
private final TestGenerationContext generationContext;
|
||||||
|
|
||||||
|
CodeWarningsTests() {
|
||||||
|
this.codeWarnings = new CodeWarnings();
|
||||||
|
this.generationContext = new TestGenerationContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void registerNoWarningDoesNotIncludeAnnotation() {
|
||||||
|
compile(method -> {
|
||||||
|
this.codeWarnings.suppress(method);
|
||||||
|
method.addStatement("$T bean = $S", String.class, "Hello");
|
||||||
|
}, compiled -> assertThat(compiled.getSourceFile()).doesNotContain("@SuppressWarnings"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
void registerWarningSuppressesIt() {
|
||||||
|
this.codeWarnings.register("deprecation");
|
||||||
|
compile(method -> {
|
||||||
|
this.codeWarnings.suppress(method);
|
||||||
|
method.addStatement("$T bean = new $T()", DeprecatedBean.class, DeprecatedBean.class);
|
||||||
|
}, compiled -> assertThat(compiled.getSourceFile())
|
||||||
|
.contains("@SuppressWarnings(\"deprecation\")"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SuppressWarnings({ "deprecation", "removal" })
|
||||||
|
void registerSeveralWarningsSuppressesThem() {
|
||||||
|
this.codeWarnings.register("deprecation");
|
||||||
|
this.codeWarnings.register("removal");
|
||||||
|
compile(method -> {
|
||||||
|
this.codeWarnings.suppress(method);
|
||||||
|
method.addStatement("$T bean = new $T()", DeprecatedBean.class, DeprecatedBean.class);
|
||||||
|
method.addStatement("$T another = new $T()", DeprecatedForRemovalBean.class, DeprecatedForRemovalBean.class);
|
||||||
|
}, compiled -> assertThat(compiled.getSourceFile())
|
||||||
|
.contains("@SuppressWarnings({ \"deprecation\", \"removal\" })"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
void detectDeprecationOnAnnotatedElementWithDeprecated() {
|
||||||
|
this.codeWarnings.detectDeprecation(DeprecatedBean.class);
|
||||||
|
assertThat(this.codeWarnings.getWarnings()).containsExactly("deprecation");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SuppressWarnings("removal")
|
||||||
|
void detectDeprecationOnAnnotatedElementWithDeprecatedForRemoval() {
|
||||||
|
this.codeWarnings.detectDeprecation(DeprecatedForRemovalBean.class);
|
||||||
|
assertThat(this.codeWarnings.getWarnings()).containsExactly("removal");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void toStringIncludeWarnings() {
|
||||||
|
this.codeWarnings.register("deprecation");
|
||||||
|
this.codeWarnings.register("rawtypes");
|
||||||
|
assertThat(this.codeWarnings).hasToString("CodeWarnings[deprecation, rawtypes]");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void compile(Consumer<Builder> method,
|
||||||
|
Consumer<Compiled> result) {
|
||||||
|
DeferredTypeBuilder typeBuilder = new DeferredTypeBuilder();
|
||||||
|
this.generationContext.getGeneratedClasses().addForFeature("TestCode", typeBuilder);
|
||||||
|
typeBuilder.set(type -> {
|
||||||
|
type.addModifiers(Modifier.PUBLIC);
|
||||||
|
Builder methodBuilder = MethodSpec.methodBuilder("apply")
|
||||||
|
.addModifiers(Modifier.PUBLIC);
|
||||||
|
method.accept(methodBuilder);
|
||||||
|
type.addMethod(methodBuilder.build());
|
||||||
|
});
|
||||||
|
this.generationContext.writeGeneratedContent();
|
||||||
|
TEST_COMPILER.with(this.generationContext).compile(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -23,6 +23,8 @@ import java.util.function.Supplier;
|
||||||
import javax.lang.model.element.Modifier;
|
import javax.lang.model.element.Modifier;
|
||||||
|
|
||||||
import org.assertj.core.api.ThrowingConsumer;
|
import org.assertj.core.api.ThrowingConsumer;
|
||||||
|
import org.junit.jupiter.api.Disabled;
|
||||||
|
import org.junit.jupiter.api.Nested;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import org.springframework.aot.generate.GeneratedClass;
|
import org.springframework.aot.generate.GeneratedClass;
|
||||||
|
|
@ -44,6 +46,12 @@ import org.springframework.beans.testfixture.beans.factory.generator.InnerCompon
|
||||||
import org.springframework.beans.testfixture.beans.factory.generator.InnerComponentConfiguration.EnvironmentAwareComponent;
|
import org.springframework.beans.testfixture.beans.factory.generator.InnerComponentConfiguration.EnvironmentAwareComponent;
|
||||||
import org.springframework.beans.testfixture.beans.factory.generator.InnerComponentConfiguration.NoDependencyComponent;
|
import org.springframework.beans.testfixture.beans.factory.generator.InnerComponentConfiguration.NoDependencyComponent;
|
||||||
import org.springframework.beans.testfixture.beans.factory.generator.SimpleConfiguration;
|
import org.springframework.beans.testfixture.beans.factory.generator.SimpleConfiguration;
|
||||||
|
import org.springframework.beans.testfixture.beans.factory.generator.deprecation.DeprecatedBean;
|
||||||
|
import org.springframework.beans.testfixture.beans.factory.generator.deprecation.DeprecatedConstructor;
|
||||||
|
import org.springframework.beans.testfixture.beans.factory.generator.deprecation.DeprecatedForRemovalBean;
|
||||||
|
import org.springframework.beans.testfixture.beans.factory.generator.deprecation.DeprecatedForRemovalConstructor;
|
||||||
|
import org.springframework.beans.testfixture.beans.factory.generator.deprecation.DeprecatedForRemovalMemberConfiguration;
|
||||||
|
import org.springframework.beans.testfixture.beans.factory.generator.deprecation.DeprecatedMemberConfiguration;
|
||||||
import org.springframework.beans.testfixture.beans.factory.generator.factory.NumberHolder;
|
import org.springframework.beans.testfixture.beans.factory.generator.factory.NumberHolder;
|
||||||
import org.springframework.beans.testfixture.beans.factory.generator.factory.NumberHolderFactoryBean;
|
import org.springframework.beans.testfixture.beans.factory.generator.factory.NumberHolderFactoryBean;
|
||||||
import org.springframework.beans.testfixture.beans.factory.generator.factory.SampleFactory;
|
import org.springframework.beans.testfixture.beans.factory.generator.factory.SampleFactory;
|
||||||
|
|
@ -57,6 +65,7 @@ import org.springframework.javapoet.ParameterizedTypeName;
|
||||||
import org.springframework.util.ReflectionUtils;
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatNoException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link InstanceSupplierCodeGenerator}.
|
* Tests for {@link InstanceSupplierCodeGenerator}.
|
||||||
|
|
@ -261,6 +270,108 @@ class InstanceSupplierCodeGeneratorTests {
|
||||||
.satisfies(hasMethodWithMode(ExecutableMode.INTROSPECT));
|
.satisfies(hasMethodWithMode(ExecutableMode.INTROSPECT));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
class DeprecationTests {
|
||||||
|
|
||||||
|
private static final TestCompiler TEST_COMPILER = TestCompiler.forSystem()
|
||||||
|
.withCompilerOptions("-Xlint:all", "-Xlint:-rawtypes", "-Werror");
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Disabled("Need to move to a separate method so that the warning can be suppressed")
|
||||||
|
void generateWhenTargetClassIsDeprecated() {
|
||||||
|
compileAndCheckWarnings(new RootBeanDefinition(DeprecatedBean.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void generateWhenTargetConstructorIsDeprecated() {
|
||||||
|
compileAndCheckWarnings(new RootBeanDefinition(DeprecatedConstructor.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void generateWhenTargetFactoryMethodIsDeprecated() {
|
||||||
|
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||||
|
.rootBeanDefinition(String.class)
|
||||||
|
.setFactoryMethodOnBean("deprecatedString", "config").getBeanDefinition();
|
||||||
|
beanFactory.registerBeanDefinition("config", BeanDefinitionBuilder
|
||||||
|
.genericBeanDefinition(DeprecatedMemberConfiguration.class).getBeanDefinition());
|
||||||
|
compileAndCheckWarnings(beanDefinition);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void generateWhenTargetFactoryMethodParameterIsDeprecated() {
|
||||||
|
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||||
|
.rootBeanDefinition(String.class)
|
||||||
|
.setFactoryMethodOnBean("deprecatedParameter", "config").getBeanDefinition();
|
||||||
|
beanFactory.registerBeanDefinition("config", BeanDefinitionBuilder
|
||||||
|
.genericBeanDefinition(DeprecatedMemberConfiguration.class).getBeanDefinition());
|
||||||
|
beanFactory.registerBeanDefinition("parameter", new RootBeanDefinition(DeprecatedBean.class));
|
||||||
|
compileAndCheckWarnings(beanDefinition);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void generateWhenTargetFactoryMethodReturnTypeIsDeprecated() {
|
||||||
|
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||||
|
.rootBeanDefinition(DeprecatedBean.class)
|
||||||
|
.setFactoryMethodOnBean("deprecatedReturnType", "config").getBeanDefinition();
|
||||||
|
beanFactory.registerBeanDefinition("config", BeanDefinitionBuilder
|
||||||
|
.genericBeanDefinition(DeprecatedMemberConfiguration.class).getBeanDefinition());
|
||||||
|
compileAndCheckWarnings(beanDefinition);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void compileAndCheckWarnings(BeanDefinition beanDefinition) {
|
||||||
|
assertThatNoException().isThrownBy(() -> compile(TEST_COMPILER, beanDefinition,
|
||||||
|
((instanceSupplier, compiled) -> {})));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
@SuppressWarnings("removal")
|
||||||
|
class DeprecationForRemovalTests {
|
||||||
|
|
||||||
|
private static final TestCompiler TEST_COMPILER = TestCompiler.forSystem()
|
||||||
|
.withCompilerOptions("-Xlint:all", "-Xlint:-rawtypes", "-Werror");
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Disabled("Need to move to a separate method so that the warning can be suppressed")
|
||||||
|
void generateWhenTargetClassIsDeprecatedForRemoval() {
|
||||||
|
compileAndCheckWarnings(new RootBeanDefinition(DeprecatedForRemovalBean.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void generateWhenTargetConstructorIsDeprecatedForRemoval() {
|
||||||
|
compileAndCheckWarnings(new RootBeanDefinition(DeprecatedForRemovalConstructor.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void generateWhenTargetFactoryMethodIsDeprecatedForRemoval() {
|
||||||
|
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||||
|
.rootBeanDefinition(String.class)
|
||||||
|
.setFactoryMethodOnBean("deprecatedString", "config").getBeanDefinition();
|
||||||
|
beanFactory.registerBeanDefinition("config", BeanDefinitionBuilder
|
||||||
|
.genericBeanDefinition(DeprecatedForRemovalMemberConfiguration.class).getBeanDefinition());
|
||||||
|
compileAndCheckWarnings(beanDefinition);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void generateWhenTargetFactoryMethodParameterIsDeprecatedForRemoval() {
|
||||||
|
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||||
|
.rootBeanDefinition(String.class)
|
||||||
|
.setFactoryMethodOnBean("deprecatedParameter", "config").getBeanDefinition();
|
||||||
|
beanFactory.registerBeanDefinition("config", BeanDefinitionBuilder
|
||||||
|
.genericBeanDefinition(DeprecatedForRemovalMemberConfiguration.class).getBeanDefinition());
|
||||||
|
beanFactory.registerBeanDefinition("parameter", new RootBeanDefinition(DeprecatedForRemovalBean.class));
|
||||||
|
compileAndCheckWarnings(beanDefinition);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void compileAndCheckWarnings(BeanDefinition beanDefinition) {
|
||||||
|
assertThatNoException().isThrownBy(() -> compile(TEST_COMPILER, beanDefinition,
|
||||||
|
((instanceSupplier, compiled) -> {})));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private ReflectionHints getReflectionHints() {
|
private ReflectionHints getReflectionHints() {
|
||||||
return this.generationContext.getRuntimeHints().reflection();
|
return this.generationContext.getRuntimeHints().reflection();
|
||||||
}
|
}
|
||||||
|
|
@ -285,6 +396,11 @@ class InstanceSupplierCodeGeneratorTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void compile(BeanDefinition beanDefinition, BiConsumer<InstanceSupplier<?>, Compiled> result) {
|
private void compile(BeanDefinition beanDefinition, BiConsumer<InstanceSupplier<?>, Compiled> result) {
|
||||||
|
compile(TestCompiler.forSystem(), beanDefinition, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void compile(TestCompiler testCompiler, BeanDefinition beanDefinition,
|
||||||
|
BiConsumer<InstanceSupplier<?>, Compiled> result) {
|
||||||
|
|
||||||
DefaultListableBeanFactory freshBeanFactory = new DefaultListableBeanFactory(this.beanFactory);
|
DefaultListableBeanFactory freshBeanFactory = new DefaultListableBeanFactory(this.beanFactory);
|
||||||
freshBeanFactory.registerBeanDefinition("testBean", beanDefinition);
|
freshBeanFactory.registerBeanDefinition("testBean", beanDefinition);
|
||||||
|
|
@ -306,8 +422,8 @@ class InstanceSupplierCodeGeneratorTests {
|
||||||
.addStatement("return $L", generatedCode).build());
|
.addStatement("return $L", generatedCode).build());
|
||||||
});
|
});
|
||||||
this.generationContext.writeGeneratedContent();
|
this.generationContext.writeGeneratedContent();
|
||||||
TestCompiler.forSystem().with(this.generationContext).compile(compiled ->
|
testCompiler.with(this.generationContext).compile(compiled -> result.accept(
|
||||||
result.accept((InstanceSupplier<?>) compiled.getInstance(Supplier.class).get(), compiled));
|
(InstanceSupplier<?>) compiled.getInstance(Supplier.class).get(), compiled));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2023 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.beans.testfixture.beans.factory.generator.deprecation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A sample bean that's fully deprecated.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public class DeprecatedBean {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2023 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.beans.testfixture.beans.factory.generator.deprecation;
|
||||||
|
|
||||||
|
import org.springframework.core.env.Environment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A sample whose factory method (constructor) is deprecated.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
*/
|
||||||
|
public class DeprecatedConstructor {
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public DeprecatedConstructor(Environment environment) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2023 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.beans.testfixture.beans.factory.generator.deprecation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A sample bean that's fully deprecated for removal.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
*/
|
||||||
|
@Deprecated(forRemoval = true)
|
||||||
|
public class DeprecatedForRemovalBean {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2023 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.beans.testfixture.beans.factory.generator.deprecation;
|
||||||
|
|
||||||
|
import org.springframework.core.env.Environment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A sample whose factory method (constructor) is deprecated for removal
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
*/
|
||||||
|
public class DeprecatedForRemovalConstructor {
|
||||||
|
|
||||||
|
@Deprecated(forRemoval = true)
|
||||||
|
public DeprecatedForRemovalConstructor(Environment environment) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2023 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.beans.testfixture.beans.factory.generator.deprecation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class with deprecated members for removal to test various use cases.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
*/
|
||||||
|
public class DeprecatedForRemovalMemberConfiguration {
|
||||||
|
|
||||||
|
@Deprecated(forRemoval = true)
|
||||||
|
public String deprecatedString() {
|
||||||
|
return "deprecated";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String deprecatedParameter(DeprecatedForRemovalBean bean) {
|
||||||
|
return bean.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2023 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.beans.testfixture.beans.factory.generator.deprecation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class with deprecated members to test various use cases.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
*/
|
||||||
|
public class DeprecatedMemberConfiguration {
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public String deprecatedString() {
|
||||||
|
return "deprecated";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String deprecatedParameter(DeprecatedBean bean) {
|
||||||
|
return bean.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public DeprecatedBean deprecatedReturnType() {
|
||||||
|
return new DeprecatedBean();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
Reference in New Issue