Skip to content

Commit

Permalink
add general coding rule DEPRECATED_API_SHOULD_NOT_BE_USED
Browse files Browse the repository at this point in the history
Signed-off-by: Manfred Hanke <Manfred.Hanke@tngtech.com>
  • Loading branch information
hankem authored and codecholeric committed Dec 27, 2022
1 parent e63882d commit 3d48c64
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -39,13 +40,15 @@
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;
import static com.tngtech.archunit.core.domain.properties.HasType.Functions.GET_RAW_TYPE;
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;
Expand Down Expand Up @@ -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 should be a better alternative");
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@
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;

public class GeneralCodingRulesTest {

Expand Down Expand Up @@ -119,4 +120,64 @@ 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() {
@SuppressWarnings("DeprecatedIsStillUsed")
class ClassWithDeprecatedMembers {
@Deprecated
int target;

@Deprecated
ClassWithDeprecatedMembers() {
}

@Deprecated
void target() {
}
}
@SuppressWarnings("DeprecatedIsStillUsed")
@Deprecated
class DeprecatedClass {
int target;

void target() {
}
}
@SuppressWarnings("unused")
class Origin {
@DeprecatedAnnotation
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 should be a better alternative")
.checking(new ClassFileImporter().importClasses(Origin.class, ClassWithDeprecatedMembers.class, DeprecatedClass.class))
.hasViolations(10)
.hasViolationContaining("%s calls constructor <%s.%s>", violatingMethod, ClassWithDeprecatedMembers.class.getName(), innerClassConstructor)
.hasViolationContaining("%s gets field <%s.target>", violatingMethod, ClassWithDeprecatedMembers.class.getName())
.hasViolationContaining("%s sets field <%s.target>", violatingMethod, ClassWithDeprecatedMembers.class.getName())
.hasViolationContaining("%s calls method <%s.target()>", violatingMethod, ClassWithDeprecatedMembers.class.getName())
.hasViolationContaining("%s calls constructor <%s.%s>", violatingMethod, DeprecatedClass.class.getName(), innerClassConstructor)
.hasViolationContaining("%s gets field <%s.target>", violatingMethod, DeprecatedClass.class.getName())
.hasViolationContaining("%s sets field <%s.target>", violatingMethod, DeprecatedClass.class.getName())
.hasViolationContaining("%s calls method <%s.target()>", violatingMethod, DeprecatedClass.class.getName())
.hasViolationContaining("%s is annotated with <%s>", violatingMethod, DeprecatedAnnotation.class.getName())
.hasViolationContaining("%s references class object <%s>", violatingMethod, DeprecatedClass.class.getName());
}

@Deprecated
@SuppressWarnings("DeprecatedIsStillUsed")
private @interface DeprecatedAnnotation {
}
}

0 comments on commit 3d48c64

Please sign in to comment.