Detect deprecation on enclosing classes as well

This commit improves CodeWarnings so that it detects if an enclosing
class is deprecated. Previously, it would only consider the annotated
element itself and the enclosing element is important for a class as
it is required to refer to it.

Closes gh-33273
This commit is contained in:
Stéphane Nicoll 2024-07-25 16:28:40 +02:00
parent bcdc991838
commit 52849819de
4 changed files with 54 additions and 11 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.
@ -59,7 +59,7 @@ class CodeWarnings {
*/
public CodeWarnings detectDeprecation(AnnotatedElement... elements) {
for (AnnotatedElement element : elements) {
register(element.getAnnotation(Deprecated.class));
registerDeprecationIfNecessary(element);
}
return this;
}
@ -113,6 +113,16 @@ class CodeWarnings {
return Collections.unmodifiableSet(this.warnings);
}
private void registerDeprecationIfNecessary(@Nullable AnnotatedElement element) {
if (element == null) {
return;
}
register(element.getAnnotation(Deprecated.class));
if (element instanceof Class<?> type) {
registerDeprecationIfNecessary(type.getEnclosingClass());
}
}
private void register(@Nullable Deprecated annotation) {
if (annotation != null) {
if (annotation.forRemoval()) {

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.
@ -97,6 +97,13 @@ class CodeWarningsTests {
assertThat(this.codeWarnings.getWarnings()).containsExactly("deprecation");
}
@Test
@SuppressWarnings("deprecation")
void detectDeprecationOnAnnotatedElementWhoseEnclosingElementIsDeprecated() {
this.codeWarnings.detectDeprecation(DeprecatedBean.Nested.class);
assertThat(this.codeWarnings.getWarnings()).containsExactly("deprecation");
}
@Test
@SuppressWarnings("removal")
void detectDeprecationOnAnnotatedElementWithDeprecatedForRemoval() {
@ -104,6 +111,13 @@ class CodeWarningsTests {
assertThat(this.codeWarnings.getWarnings()).containsExactly("removal");
}
@Test
@SuppressWarnings("removal")
void detectDeprecationOnAnnotatedElementWhoseEnclosingElementIsDeprecatedForRemoval() {
this.codeWarnings.detectDeprecation(DeprecatedForRemovalBean.Nested.class);
assertThat(this.codeWarnings.getWarnings()).containsExactly("removal");
}
@ParameterizedTest
@MethodSource("resolvableTypesWithDeprecated")
void detectDeprecationOnResolvableTypeWithDeprecated(ResolvableType resolvableType) {
@ -113,11 +127,17 @@ class CodeWarningsTests {
@SuppressWarnings("deprecation")
static Stream<Arguments> resolvableTypesWithDeprecated() {
Class<?> deprecatedBean = DeprecatedBean.class;
Class<?> nested = DeprecatedBean.Nested.class;
return Stream.of(
Arguments.of(ResolvableType.forClass(DeprecatedBean.class)),
Arguments.of(ResolvableType.forClassWithGenerics(GenericBean.class, DeprecatedBean.class)),
Arguments.of(ResolvableType.forClass(deprecatedBean)),
Arguments.of(ResolvableType.forClass(nested)),
Arguments.of(ResolvableType.forClassWithGenerics(GenericBean.class, deprecatedBean)),
Arguments.of(ResolvableType.forClassWithGenerics(GenericBean.class, nested)),
Arguments.of(ResolvableType.forClassWithGenerics(GenericBean.class,
ResolvableType.forClassWithGenerics(GenericBean.class, DeprecatedBean.class)))
ResolvableType.forClassWithGenerics(GenericBean.class, deprecatedBean))),
Arguments.of(ResolvableType.forClassWithGenerics(GenericBean.class,
ResolvableType.forClassWithGenerics(GenericBean.class, nested)))
);
}
@ -130,11 +150,17 @@ class CodeWarningsTests {
@SuppressWarnings("removal")
static Stream<Arguments> resolvableTypesWithDeprecatedForRemoval() {
Class<?> deprecatedBean = DeprecatedForRemovalBean.class;
Class<?> nested = DeprecatedForRemovalBean.Nested.class;
return Stream.of(
Arguments.of(ResolvableType.forClass(DeprecatedForRemovalBean.class)),
Arguments.of(ResolvableType.forClassWithGenerics(GenericBean.class, DeprecatedForRemovalBean.class)),
Arguments.of(ResolvableType.forClass(deprecatedBean)),
Arguments.of(ResolvableType.forClass(nested)),
Arguments.of(ResolvableType.forClassWithGenerics(GenericBean.class, deprecatedBean)),
Arguments.of(ResolvableType.forClassWithGenerics(GenericBean.class, nested)),
Arguments.of(ResolvableType.forClassWithGenerics(GenericBean.class,
ResolvableType.forClassWithGenerics(GenericBean.class, DeprecatedForRemovalBean.class)))
ResolvableType.forClassWithGenerics(GenericBean.class, deprecatedBean))),
Arguments.of(ResolvableType.forClassWithGenerics(GenericBean.class,
ResolvableType.forClassWithGenerics(GenericBean.class, nested)))
);
}

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.
@ -23,4 +23,8 @@ package org.springframework.beans.testfixture.beans.factory.generator.deprecatio
*/
@Deprecated
public class DeprecatedBean {
// This isn't flag deprecated on purpose
public static class Nested {}
}

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.
@ -23,4 +23,7 @@ package org.springframework.beans.testfixture.beans.factory.generator.deprecatio
*/
@Deprecated(forRemoval = true)
public class DeprecatedForRemovalBean {
// This isn't flag deprecated on purpose
public static class Nested {}
}