From cbef73699bf362c3a9abc1048dd423448cf19802 Mon Sep 17 00:00:00 2001 From: Manfred Hanke Date: Fri, 25 Nov 2022 13:54:03 +0100 Subject: [PATCH] add general coding rule DEPRECATED_API_SHOULD_NOT_BE_USED Signed-off-by: Manfred Hanke --- .../archunit/library/GeneralCodingRules.java | 14 +++++ .../library/GeneralCodingRulesTest.java | 51 +++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/archunit/src/main/java/com/tngtech/archunit/library/GeneralCodingRules.java b/archunit/src/main/java/com/tngtech/archunit/library/GeneralCodingRules.java index ec2fdf03c7..df4003048c 100644 --- a/archunit/src/main/java/com/tngtech/archunit/library/GeneralCodingRules.java +++ b/archunit/src/main/java/com/tngtech/archunit/library/GeneralCodingRules.java @@ -22,6 +22,7 @@ import com.tngtech.archunit.PublicAPI; import com.tngtech.archunit.core.domain.AccessTarget.FieldAccessTarget; +import com.tngtech.archunit.core.domain.JavaAccess; import com.tngtech.archunit.core.domain.JavaAccess.Functions.Get; import com.tngtech.archunit.core.domain.JavaClass; import com.tngtech.archunit.core.domain.JavaField; @@ -39,6 +40,7 @@ import static com.tngtech.archunit.core.domain.JavaCall.Predicates.target; import static com.tngtech.archunit.core.domain.JavaClass.Predicates.assignableTo; import static com.tngtech.archunit.core.domain.JavaClass.Predicates.resideInAPackage; +import static com.tngtech.archunit.core.domain.properties.CanBeAnnotated.Predicates.annotatedWith; import static com.tngtech.archunit.core.domain.properties.HasName.Predicates.name; import static com.tngtech.archunit.core.domain.properties.HasOwner.Predicates.With.owner; import static com.tngtech.archunit.core.domain.properties.HasParameterTypes.Predicates.rawParameterTypes; @@ -46,6 +48,7 @@ import static com.tngtech.archunit.lang.ConditionEvent.createMessage; import static com.tngtech.archunit.lang.SimpleConditionEvent.violated; import static com.tngtech.archunit.lang.conditions.ArchConditions.accessField; +import static com.tngtech.archunit.lang.conditions.ArchConditions.accessTargetWhere; import static com.tngtech.archunit.lang.conditions.ArchConditions.beAnnotatedWith; import static com.tngtech.archunit.lang.conditions.ArchConditions.callCodeUnitWhere; import static com.tngtech.archunit.lang.conditions.ArchConditions.callMethodWhere; @@ -498,4 +501,15 @@ public void check(JavaClass implementationClass, ConditionEvents events) { public static final ArchRule ASSERTIONS_SHOULD_HAVE_DETAIL_MESSAGE = noClasses().should().callConstructor(AssertionError.class /* without detailMessage */) .because("assertions should have a detail message"); + + /** + * A rule checking that no class accesses {@link Deprecated} members (i.e. calls methods or constructors, or accesses fields) + * or in other ways depends on {@link Deprecated} classes. + */ + @PublicAPI(usage = ACCESS) + public static final ArchRule DEPRECATED_API_SHOULD_NOT_BE_USED = + noClasses() + .should(accessTargetWhere(JavaAccess.Predicates.target(annotatedWith(Deprecated.class))).as("access @Deprecated members")) + .orShould(dependOnClassesThat(annotatedWith(Deprecated.class)).as("depend on @Deprecated classes")) + .because("there is probably a better alternative"); } diff --git a/archunit/src/test/java/com/tngtech/archunit/library/GeneralCodingRulesTest.java b/archunit/src/test/java/com/tngtech/archunit/library/GeneralCodingRulesTest.java index 352cffe787..7b5368384d 100644 --- a/archunit/src/test/java/com/tngtech/archunit/library/GeneralCodingRulesTest.java +++ b/archunit/src/test/java/com/tngtech/archunit/library/GeneralCodingRulesTest.java @@ -12,7 +12,9 @@ import com.tngtech.archunit.library.testclasses.packages.incorrect.wrongsubdir.defaultsuffix.subdir.ImplementationClassWithWrongTestClassPackageTest; import org.junit.Test; +import static com.tngtech.archunit.core.domain.JavaConstructor.CONSTRUCTOR_NAME; import static com.tngtech.archunit.library.GeneralCodingRules.ASSERTIONS_SHOULD_HAVE_DETAIL_MESSAGE; +import static com.tngtech.archunit.library.GeneralCodingRules.DEPRECATED_API_SHOULD_NOT_BE_USED; import static com.tngtech.archunit.library.GeneralCodingRules.testClassesShouldResideInTheSamePackageAsImplementation; import static com.tngtech.archunit.testutil.Assertions.assertThatRule; import static java.util.regex.Pattern.quote; @@ -115,4 +117,53 @@ void f() { .checking(new ClassFileImporter().importClasses(ValidAssertions.class)) .hasNoViolation(); } + + @Test + public void DEPRECATED_API_SHOULD_NOT_BE_USED_should_fail_on_call_to_deprecated_method() { + class ClassWithDeprecatedMembers { + @Deprecated + int target; + + @Deprecated + ClassWithDeprecatedMembers() { + } + + @Deprecated + void target() { + } + } + @Deprecated + class DeprecatedClass { + int target; + + void target() { + } + } + class Origin { + void origin() { + ClassWithDeprecatedMembers instanceOfClassWithDeprecatedMembers = new ClassWithDeprecatedMembers(); + instanceOfClassWithDeprecatedMembers.target++; + instanceOfClassWithDeprecatedMembers.target(); + DeprecatedClass instanceOfDeprecatedClass = new DeprecatedClass(); + instanceOfDeprecatedClass.target++; + instanceOfDeprecatedClass.target(); + Class deprecatedClass = DeprecatedClass.class; + } + } + String innerClassConstructor = CONSTRUCTOR_NAME + "(" + GeneralCodingRulesTest.class.getName() + ")"; + String violatingMethod = "Method <" + Origin.class.getName() + ".origin()>"; + assertThatRule(DEPRECATED_API_SHOULD_NOT_BE_USED) + .hasDescriptionContaining("no classes should access @Deprecated members or should depend on @Deprecated classes, because there is probably a better alternative") + .checking(new ClassFileImporter().importClasses(Origin.class, ClassWithDeprecatedMembers.class, DeprecatedClass.class)) + .hasViolations(9) + .hasViolationMatching(quote(violatingMethod + " calls constructor <" + ClassWithDeprecatedMembers.class.getName() + "." + innerClassConstructor + "> in ") + ".*") + .hasViolationMatching(quote(violatingMethod + " calls method <" + ClassWithDeprecatedMembers.class.getName() + ".target()> in ") + ".*") + .hasViolationMatching(quote(violatingMethod + " gets field <" + ClassWithDeprecatedMembers.class.getName() + ".target> in ") + ".*") + .hasViolationMatching(quote(violatingMethod + " sets field <" + ClassWithDeprecatedMembers.class.getName() + ".target> in ") + ".*") + .hasViolationMatching(quote(violatingMethod + " calls constructor <" + DeprecatedClass.class.getName() + "." + innerClassConstructor + "> in ") + ".*") + .hasViolationMatching(quote(violatingMethod + " calls method <" + DeprecatedClass.class.getName() + ".target()> in ") + ".*") + .hasViolationMatching(quote(violatingMethod + " gets field <" + DeprecatedClass.class.getName() + ".target> in ") + ".*") + .hasViolationMatching(quote(violatingMethod + " sets field <" + DeprecatedClass.class.getName() + ".target> in ") + ".*") + .hasViolationMatching(quote(violatingMethod + " references class object <" + DeprecatedClass.class.getName() + "> in ") + ".*"); + } }