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:
@@ -164,11 +164,14 @@ class BeanDefinitionMethodGenerator {
|
||||
|
||||
this.aotContributions.forEach(aotContribution -> aotContribution.applyTo(generationContext, codeGenerator));
|
||||
|
||||
CodeWarnings codeWarnings = new CodeWarnings();
|
||||
codeWarnings.detectDeprecation(this.registeredBean.getBeanClass());
|
||||
return generatedMethods.add("getBeanDefinition", method -> {
|
||||
method.addJavadoc("Get the $L definition for '$L'.",
|
||||
(this.registeredBean.isInnerBean() ? "inner-bean" : "bean"),
|
||||
getName());
|
||||
method.addModifiers(modifier, Modifier.STATIC);
|
||||
codeWarnings.suppress(method);
|
||||
method.returns(BeanDefinition.class);
|
||||
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.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.Arrays;
|
||||
import java.util.function.Consumer;
|
||||
@@ -189,11 +190,15 @@ public class InstanceSupplierCodeGenerator {
|
||||
private CodeBlock generateCodeForInaccessibleConstructor(String beanName, Class<?> beanClass,
|
||||
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());
|
||||
|
||||
GeneratedMethod generatedMethod = generateGetInstanceSupplierMethod(method -> {
|
||||
method.addJavadoc("Get the bean instance supplier for '$L'.", beanName);
|
||||
method.addModifiers(PRIVATE_STATIC);
|
||||
codeWarnings.suppress(method);
|
||||
method.returns(ParameterizedTypeName.get(BeanInstanceSupplier.class, beanClass));
|
||||
int parameterOffset = (!dependsOnBean) ? 0 : 1;
|
||||
method.addStatement(generateResolverForConstructor(beanClass, constructor, parameterOffset));
|
||||
@@ -206,8 +211,12 @@ public class InstanceSupplierCodeGenerator {
|
||||
String beanName, Class<?> beanClass, Constructor<?> constructor, Class<?> declaringClass,
|
||||
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.addModifiers(modifiers);
|
||||
codeWarnings.suppress(method);
|
||||
method.returns(ParameterizedTypeName.get(BeanInstanceSupplier.class, beanClass));
|
||||
|
||||
int parameterOffset = (!dependsOnBean) ? 0 : 1;
|
||||
@@ -300,9 +309,13 @@ public class InstanceSupplierCodeGenerator {
|
||||
|
||||
String factoryMethodName = factoryMethod.getName();
|
||||
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.addModifiers(modifiers);
|
||||
codeWarnings.suppress(method);
|
||||
method.returns(ParameterizedTypeName.get(BeanInstanceSupplier.class, suppliedType));
|
||||
|
||||
CodeBlock.Builder code = CodeBlock.builder();
|
||||
|
||||
Reference in New Issue
Block a user