Skip to content

Commit 469eef6

Browse files
committed
Check that Deprecated have a "since" attribute
This commit enforces a new architecture rule. Methods and Types annotated with `@Deprecated` should always have a "since" attribute. Signed-off-by: Brian Clozel <brian.clozel@broadcom.com>
1 parent 941b791 commit 469eef6

File tree

2 files changed

+47
-13
lines changed

2 files changed

+47
-13
lines changed

buildSrc/src/main/java/org/springframework/build/architecture/ArchitectureCheck.java

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,16 @@
1616

1717
package org.springframework.build.architecture;
1818

19-
import com.tngtech.archunit.core.domain.JavaClasses;
20-
import com.tngtech.archunit.core.importer.ClassFileImporter;
21-
import com.tngtech.archunit.lang.ArchRule;
22-
import com.tngtech.archunit.lang.EvaluationResult;
2319
import java.io.File;
2420
import java.io.IOException;
2521
import java.nio.file.Files;
2622
import java.nio.file.StandardOpenOption;
2723
import java.util.List;
24+
25+
import com.tngtech.archunit.core.domain.JavaClasses;
26+
import com.tngtech.archunit.core.importer.ClassFileImporter;
27+
import com.tngtech.archunit.lang.ArchRule;
28+
import com.tngtech.archunit.lang.EvaluationResult;
2829
import org.gradle.api.DefaultTask;
2930
import org.gradle.api.GradleException;
3031
import org.gradle.api.Task;
@@ -46,6 +47,8 @@
4647

4748
import static org.springframework.build.architecture.ArchitectureRules.allPackagesShouldBeFreeOfTangles;
4849
import static org.springframework.build.architecture.ArchitectureRules.classesShouldNotImportForbiddenTypes;
50+
import static org.springframework.build.architecture.ArchitectureRules.deprecatedMethodsShouldHaveSinceAttribute;
51+
import static org.springframework.build.architecture.ArchitectureRules.deprecatedTypesShouldHaveSinceAttribute;
4952
import static org.springframework.build.architecture.ArchitectureRules.javaClassesShouldNotImportKotlinAnnotations;
5053
import static org.springframework.build.architecture.ArchitectureRules.noClassesShouldCallStringToLowerCaseWithoutLocale;
5154
import static org.springframework.build.architecture.ArchitectureRules.noClassesShouldCallStringToUpperCaseWithoutLocale;
@@ -69,7 +72,10 @@ public ArchitectureCheck() {
6972
javaClassesShouldNotImportKotlinAnnotations(),
7073
allPackagesShouldBeFreeOfTangles(),
7174
noClassesShouldCallStringToLowerCaseWithoutLocale(),
72-
noClassesShouldCallStringToUpperCaseWithoutLocale());
75+
noClassesShouldCallStringToUpperCaseWithoutLocale(),
76+
deprecatedMethodsShouldHaveSinceAttribute(),
77+
deprecatedTypesShouldHaveSinceAttribute());
78+
7379
getRuleDescriptions().set(getRules().map((rules) -> rules.stream().map(ArchRule::getDescription).toList()));
7480
}
7581

buildSrc/src/main/java/org/springframework/build/architecture/ArchitectureRules.java

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,21 @@
1717
package org.springframework.build.architecture;
1818

1919
import com.tngtech.archunit.base.DescribedPredicate;
20+
import com.tngtech.archunit.core.domain.JavaAnnotation;
2021
import com.tngtech.archunit.core.domain.JavaClass;
2122
import com.tngtech.archunit.lang.ArchRule;
2223
import com.tngtech.archunit.lang.syntax.ArchRuleDefinition;
2324
import com.tngtech.archunit.library.dependencies.SliceAssignment;
2425
import com.tngtech.archunit.library.dependencies.SliceIdentifier;
2526
import com.tngtech.archunit.library.dependencies.SlicesRuleDefinition;
26-
import java.util.List;
2727

2828
abstract class ArchitectureRules {
2929

30+
private static final String[] SHADED_PACKAGES = {"org.springframework.asm..",
31+
"org.springframework.cglib..",
32+
"org.springframework.javapoet..",
33+
"org.springframework.objenesis.."};
34+
3035
static ArchRule allPackagesShouldBeFreeOfTangles() {
3136
return SlicesRuleDefinition.slices()
3237
.assignedFrom(new SpringSlices()).should().beFreeOfCycles();
@@ -80,18 +85,41 @@ public boolean test(JavaClass javaClass) {
8085
.allowEmptyShould(true);
8186
}
8287

83-
static class SpringSlices implements SliceAssignment {
88+
static ArchRule deprecatedMethodsShouldHaveSinceAttribute() {
89+
return ArchRuleDefinition.methods().that()
90+
.areDeclaredInClassesThat().resideOutsideOfPackages(SHADED_PACKAGES).and()
91+
.areAnnotatedWith(Deprecated.class)
92+
.should().beAnnotatedWith(deprecatedWithSinceAttribute())
93+
.allowEmptyShould(true);
94+
}
8495

85-
private final List<String> ignoredPackages = List.of("org.springframework.asm",
86-
"org.springframework.cglib",
87-
"org.springframework.javapoet",
88-
"org.springframework.objenesis");
96+
static ArchRule deprecatedTypesShouldHaveSinceAttribute() {
97+
return ArchRuleDefinition.classes().that()
98+
.resideOutsideOfPackages(SHADED_PACKAGES).and()
99+
.areAnnotatedWith(Deprecated.class)
100+
.should().beAnnotatedWith(deprecatedWithSinceAttribute())
101+
.allowEmptyShould(true);
102+
}
103+
104+
private static DescribedPredicate<JavaAnnotation> deprecatedWithSinceAttribute() {
105+
return new DescribedPredicate<JavaAnnotation>("@Deprecated(since=\"...\"") {
106+
@Override
107+
public boolean test(JavaAnnotation annotation) {
108+
if (annotation.getRawType().isEquivalentTo(Deprecated.class)) {
109+
return annotation.get("since").isPresent() &&
110+
!annotation.get("since").get().toString().isEmpty();
111+
}
112+
return true;
113+
}
114+
};
115+
}
116+
117+
static class SpringSlices implements SliceAssignment {
89118

90119
@Override
91120
public SliceIdentifier getIdentifierOf(JavaClass javaClass) {
92-
93121
String packageName = javaClass.getPackageName();
94-
for (String ignoredPackage : ignoredPackages) {
122+
for (String ignoredPackage : SHADED_PACKAGES) {
95123
if (packageName.startsWith(ignoredPackage)) {
96124
return SliceIdentifier.ignore();
97125
}

0 commit comments

Comments
 (0)