Suppress deprecated warnings for autowiring

This commit extends our handling of deprecated with AOT to autowiring.

Closes gh-33295
This commit is contained in:
Stéphane Nicoll
2024-07-31 16:48:18 +02:00
parent 321e8a58ae
commit 850a0de1b0
5 changed files with 261 additions and 38 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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.
@@ -21,6 +21,7 @@ import java.util.function.BiFunction;
import javax.lang.model.element.Modifier;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.aot.generate.MethodReference;
@@ -28,9 +29,17 @@ import org.springframework.aot.generate.MethodReference.ArgumentCodeGenerator;
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
import org.springframework.aot.test.generate.TestGenerationContext;
import org.springframework.beans.factory.aot.BeanRegistrationAotContribution;
import org.springframework.beans.factory.aot.CodeWarnings;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RegisteredBean;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.testfixture.beans.factory.annotation.DeprecatedInjectionSamples.DeprecatedFieldInjectionPointSample;
import org.springframework.beans.testfixture.beans.factory.annotation.DeprecatedInjectionSamples.DeprecatedFieldInjectionTypeSample;
import org.springframework.beans.testfixture.beans.factory.annotation.DeprecatedInjectionSamples.DeprecatedMethodInjectionPointSample;
import org.springframework.beans.testfixture.beans.factory.annotation.DeprecatedInjectionSamples.DeprecatedMethodInjectionTypeSample;
import org.springframework.beans.testfixture.beans.factory.annotation.DeprecatedInjectionSamples.DeprecatedPrivateFieldInjectionTypeSample;
import org.springframework.beans.testfixture.beans.factory.annotation.DeprecatedInjectionSamples.DeprecatedPrivateMethodInjectionTypeSample;
import org.springframework.beans.testfixture.beans.factory.annotation.DeprecatedInjectionSamples.DeprecatedSample;
import org.springframework.beans.testfixture.beans.factory.annotation.PackagePrivateFieldInjectionSample;
import org.springframework.beans.testfixture.beans.factory.annotation.PackagePrivateMethodInjectionSample;
import org.springframework.beans.testfixture.beans.factory.annotation.PrivateFieldInjectionSample;
@@ -49,6 +58,7 @@ import org.springframework.javapoet.MethodSpec;
import org.springframework.javapoet.ParameterizedTypeName;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatNoException;
/**
* Tests for {@link AutowiredAnnotationBeanPostProcessor} for AOT contributions.
@@ -199,6 +209,69 @@ class AutowiredAnnotationBeanRegistrationAotContributionTests {
assertThat(contribution).isNull();
}
@Nested
@SuppressWarnings("deprecation")
class DeprecationTests {
private static final TestCompiler TEST_COMPILER = TestCompiler.forSystem()
.withCompilerOptions("-Xlint:all", "-Xlint:-rawtypes", "-Werror");
@Test
void contributeWhenTargetClassIsDeprecated() {
RegisteredBean registeredBean = getAndApplyContribution(DeprecatedSample.class);
compileAndCheckWarnings(registeredBean);
}
@Test
void contributeWhenFieldInjectionsUsesADeprecatedType() {
RegisteredBean registeredBean = getAndApplyContribution(
DeprecatedFieldInjectionTypeSample.class);
compileAndCheckWarnings(registeredBean);
}
@Test
void contributeWhenFieldInjectionsUsesADeprecatedTypeWithReflection() {
RegisteredBean registeredBean = getAndApplyContribution(
DeprecatedPrivateFieldInjectionTypeSample.class);
compileAndCheckWarnings(registeredBean);
}
@Test
void contributeWhenFieldInjectionsIsDeprecated() {
RegisteredBean registeredBean = getAndApplyContribution(
DeprecatedFieldInjectionPointSample.class);
compileAndCheckWarnings(registeredBean);
}
@Test
void contributeWhenMethodInjectionsUsesADeprecatedType() {
RegisteredBean registeredBean = getAndApplyContribution(
DeprecatedMethodInjectionTypeSample.class);
compileAndCheckWarnings(registeredBean);
}
@Test
void contributeWhenMethodInjectionsUsesADeprecatedTypeWithReflection() {
RegisteredBean registeredBean = getAndApplyContribution(
DeprecatedPrivateMethodInjectionTypeSample.class);
compileAndCheckWarnings(registeredBean);
}
@Test
void contributeWhenMethodInjectionsIsDeprecated() {
RegisteredBean registeredBean = getAndApplyContribution(
DeprecatedMethodInjectionPointSample.class);
compileAndCheckWarnings(registeredBean);
}
private void compileAndCheckWarnings(RegisteredBean registeredBean) {
assertThatNoException().isThrownBy(() -> compile(TEST_COMPILER, registeredBean,
((instanceSupplier, compiled) -> {})));
}
}
private RegisteredBean getAndApplyContribution(Class<?> beanClass) {
RegisteredBean registeredBean = registerBean(beanClass);
BeanRegistrationAotContribution contribution = this.beanPostProcessor.processAheadOfTime(registeredBean);
@@ -218,11 +291,17 @@ class AutowiredAnnotationBeanRegistrationAotContributionTests {
return compiled.getSourceFileFromPackage(sample.getPackageName());
}
@SuppressWarnings("unchecked")
private void compile(RegisteredBean registeredBean,
BiConsumer<BiFunction<RegisteredBean, Object, Object>, Compiled> result) {
compile(TestCompiler.forSystem(), registeredBean, result);
}
@SuppressWarnings("unchecked")
private void compile(TestCompiler testCompiler, RegisteredBean registeredBean,
BiConsumer<BiFunction<RegisteredBean, Object, Object>, Compiled> result) {
Class<?> target = registeredBean.getBeanClass();
MethodReference methodReference = this.beanRegistrationCode.getInstancePostProcessors().get(0);
CodeWarnings codeWarnings = new CodeWarnings();
this.beanRegistrationCode.getTypeBuilder().set(type -> {
CodeBlock methodInvocation = methodReference.toInvokeCodeBlock(
ArgumentCodeGenerator.of(RegisteredBean.class, "registeredBean").and(target, "instance"),
@@ -235,10 +314,11 @@ class AutowiredAnnotationBeanRegistrationAotContributionTests {
.addParameter(target, "instance").returns(target)
.addStatement("return $L", methodInvocation)
.build());
codeWarnings.detectDeprecation(target);
codeWarnings.suppress(type);
});
this.generationContext.writeGeneratedContent();
TestCompiler.forSystem().with(this.generationContext).compile(compiled ->
testCompiler.with(this.generationContext).printFiles(System.out).compile(compiled ->
result.accept(compiled.getInstance(BiFunction.class), compiled));
}

View File

@@ -34,8 +34,10 @@ import org.springframework.beans.testfixture.beans.factory.generator.deprecation
import org.springframework.core.ResolvableType;
import org.springframework.core.test.tools.Compiled;
import org.springframework.core.test.tools.TestCompiler;
import org.springframework.javapoet.FieldSpec;
import org.springframework.javapoet.MethodSpec;
import org.springframework.javapoet.MethodSpec.Builder;
import org.springframework.javapoet.TypeSpec;
import static org.assertj.core.api.Assertions.assertThat;
@@ -59,30 +61,49 @@ class CodeWarningsTests {
}
@Test
void registerNoWarningDoesNotIncludeAnnotation() {
compile(method -> {
void registerNoWarningDoesNotIncludeAnnotationOnMethod() {
compileWithMethod(method -> {
this.codeWarnings.suppress(method);
method.addStatement("$T bean = $S", String.class, "Hello");
}, compiled -> assertThat(compiled.getSourceFile()).doesNotContain("@SuppressWarnings"));
}
@Test
void registerNoWarningDoesNotIncludeAnnotationOnType() {
compile(type -> {
this.codeWarnings.suppress(type);
type.addField(FieldSpec.builder(String.class, "type").build());
}, compiled -> assertThat(compiled.getSourceFile()).doesNotContain("@SuppressWarnings"));
}
@Test
@SuppressWarnings("deprecation")
void registerWarningSuppressesIt() {
void registerWarningSuppressesItOnMethod() {
this.codeWarnings.register("deprecation");
compile(method -> {
compileWithMethod(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")
void registerWarningSuppressesItOnType() {
this.codeWarnings.register("deprecation");
compile(type -> {
this.codeWarnings.suppress(type);
type.addField(FieldSpec.builder(DeprecatedBean.class, "bean").build());
}, compiled -> assertThat(compiled.getSourceFile())
.contains("@SuppressWarnings(\"deprecation\")"));
}
@Test
@SuppressWarnings({ "deprecation", "removal" })
void registerSeveralWarningsSuppressesThem() {
void registerSeveralWarningsSuppressesThemOnMethod() {
this.codeWarnings.register("deprecation");
this.codeWarnings.register("removal");
compile(method -> {
compileWithMethod(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);
@@ -90,6 +111,19 @@ class CodeWarningsTests {
.contains("@SuppressWarnings({ \"deprecation\", \"removal\" })"));
}
@Test
@SuppressWarnings({ "deprecation", "removal" })
void registerSeveralWarningsSuppressesThemOnType() {
this.codeWarnings.register("deprecation");
this.codeWarnings.register("removal");
compile(type -> {
this.codeWarnings.suppress(type);
type.addField(FieldSpec.builder(DeprecatedBean.class, "bean").build());
type.addField(FieldSpec.builder(DeprecatedForRemovalBean.class, "another").build());
}, compiled -> assertThat(compiled.getSourceFile())
.contains("@SuppressWarnings({ \"deprecation\", \"removal\" })"));
}
@Test
@SuppressWarnings("deprecation")
void detectDeprecationOnAnnotatedElementWithDeprecated() {
@@ -171,16 +205,20 @@ class CodeWarningsTests {
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 -> {
private void compileWithMethod(Consumer<Builder> method, Consumer<Compiled> result) {
compile(type -> {
type.addModifiers(Modifier.PUBLIC);
Builder methodBuilder = MethodSpec.methodBuilder("apply")
.addModifiers(Modifier.PUBLIC);
method.accept(methodBuilder);
type.addMethod(methodBuilder.build());
});
}, result);
}
private void compile(Consumer<TypeSpec.Builder> type, Consumer<Compiled> result) {
DeferredTypeBuilder typeBuilder = new DeferredTypeBuilder();
this.generationContext.getGeneratedClasses().addForFeature("TestCode", typeBuilder);
typeBuilder.set(type);
this.generationContext.writeGeneratedContent();
TEST_COMPILER.with(this.generationContext).compile(result);
}