Browse Source
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-29597pull/31407/head
Stéphane Nicoll
1 year ago
12 changed files with 614 additions and 3 deletions
@ -0,0 +1,125 @@
@@ -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(); |
||||
} |
||||
|
||||
} |
||||
|
@ -0,0 +1,123 @@
@@ -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); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,27 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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