From 671f55260657cee134a5b53d5af781da1414b78d Mon Sep 17 00:00:00 2001 From: mnhock Date: Sun, 14 Jul 2024 21:18:36 +0200 Subject: [PATCH] Provide new Java Rule checking Methods from not Throwing Specific Exceptions in Specific Classes Closes gh-76 --- docs/USERGUIDE.md | 67 +++++++++++-------- .../enofex/taikai/java/JavaConfigurer.java | 27 ++++++++ src/test/java/com/enofex/taikai/Usage.java | 1 + 3 files changed, 68 insertions(+), 27 deletions(-) diff --git a/docs/USERGUIDE.md b/docs/USERGUIDE.md index 1f8cbfe..9f6bbc9 100644 --- a/docs/USERGUIDE.md +++ b/docs/USERGUIDE.md @@ -105,34 +105,35 @@ Taikai's architecture rules cover a wide range of categories to enforce best pra The default mode is `WITHOUT_TESTS`, which excludes test classes from the import check. -| Category | Method Name | Rule Description | -|----------|--------------------------------------------------------|-----------------------------------------------------------------------------------------------------------| -| General | `classesShouldImplementHashCodeAndEquals` | Classes should implement `hashCode` and `equals` together. | -| General | `classesShouldResideInPackage` | Classes matching specific naming patterns should reside in a specified package. | -| General | `classesAnnotatedWithShouldResideInPackage` | Classes annotated with a specific annotation should reside in a specified package. | -| General | `classesShouldResideOutsidePackage` | Classes matching specific naming patterns should reside outside a specified package. | -| General | `classesShouldBeAnnotatedWith` | Classes matching specific naming patterns should be annotated with a specified annotation. | -| General | `fieldsShouldNotBePublic` | Fields should not be `public`, except constants. | -| General | `methodsShouldNotDeclareGenericExceptions` | Methods should not declare generic exceptions, like `Exception` or `RuntimeException`. | -| General | `noUsageOf` | Disallow usage of specific classes. | -| General | `noUsageOf` | Disallow usage of specific classes by class reference. | -| General | `noUsageOfDeprecatedAPIs` | No usage of deprecated APIs annotated with `@Deprecated`. | -| General | `noUsageOfSystemOutOrErr` | Disallow usage of `System.out` or `System.err`. | +| Category | Method Name | Rule Description | +|----------|--------------------------------------------------------|--------------------------------------------------------------------------------------------------------------| +| General | `classesShouldImplementHashCodeAndEquals` | Classes should implement `hashCode` and `equals` together. | +| General | `classesShouldResideInPackage` | Classes matching specific naming patterns should reside in a specified package. | +| General | `classesAnnotatedWithShouldResideInPackage` | Classes annotated with a specific annotation should reside in a specified package. | +| General | `classesShouldResideOutsidePackage` | Classes matching specific naming patterns should reside outside a specified package. | +| General | `classesShouldBeAnnotatedWith` | Classes matching specific naming patterns should be annotated with a specified annotation. | +| General | `fieldsShouldNotBePublic` | Fields should not be `public`, except constants. | +| General | `methodsShouldNotDeclareGenericExceptions` | Methods should not declare generic exceptions, like `Exception` or `RuntimeException`. | +| General | `methodsShouldNotDeclareException` | Methods with names matching a specified pattern should not declare a specified exception type. | +| General | `noUsageOf` | Disallow usage of specific classes. | +| General | `noUsageOf` | Disallow usage of specific classes by class reference. | +| General | `noUsageOfDeprecatedAPIs` | No usage of deprecated APIs annotated with `@Deprecated`. | +| General | `noUsageOfSystemOutOrErr` | Disallow usage of `System.out` or `System.err`. | | General | `utilityClassesShouldBeFinalAndHavePrivateConstructor` | Utility classes with only `static` methods (except `main`) should be `final` and have a private constructor. | -| General | `finalClassesShouldNotHaveProtectedMembers` | Classes declared as `final` should not contain any `protected` members. | -| General | `serialVersionUIDShouldBeStaticFinalLong` | Fields named `serialVersionUID` should be declared as `static final long`. | -| Imports | `shouldHaveNoCycles` | No cyclic dependencies in imports. | -| Imports | `shouldNotImport` | Disallow specific imports (e.g., `..shaded..`). | -| Naming | `packagesShouldMatch` | Packages should match the specified regex pattern (e.g., `com.company.yourproject..`). | -| Naming | `classesShouldNotMatch` | Classes should not match specific naming patterns (e.g., `.*Impl`). | -| Naming | `classesAnnotatedWithShouldMatch` | Classes annotated with a specific annotation should match specific naming patterns. | -| Naming | `methodsShouldNotMatch` | Methods should not match specific naming patterns. | -| Naming | `methodsAnnotatedWithShouldMatch` | Methods annotated with a specific annotation should match specific naming patterns. | -| Naming | `fieldsShouldNotMatch` | Fields should not match specific naming patterns. | -| Naming | `fieldsShouldMatch` | Fields should match specific naming patterns for specific classes. | -| Naming | `fieldsAnnotatedWithShouldMatch` | Fields annotated with a specific annotation should match specific naming patterns. | -| Naming | `constantsShouldFollowConventions` | Constants should follow naming conventions, except `serialVersionUID`. | -| Naming | `interfacesShouldNotHavePrefixI` | Interfaces should not have the prefix `I`. | +| General | `finalClassesShouldNotHaveProtectedMembers` | Classes declared as `final` should not contain any `protected` members. | +| General | `serialVersionUIDShouldBeStaticFinalLong` | Fields named `serialVersionUID` should be declared as `static final long`. | +| Imports | `shouldHaveNoCycles` | No cyclic dependencies in imports. | +| Imports | `shouldNotImport` | Disallow specific imports (e.g., `..shaded..`). | +| Naming | `packagesShouldMatch` | Packages should match the specified regex pattern (e.g., `com.company.yourproject..`). | +| Naming | `classesShouldNotMatch` | Classes should not match specific naming patterns (e.g., `.*Impl`). | +| Naming | `classesAnnotatedWithShouldMatch` | Classes annotated with a specific annotation should match specific naming patterns. | +| Naming | `methodsShouldNotMatch` | Methods should not match specific naming patterns. | +| Naming | `methodsAnnotatedWithShouldMatch` | Methods annotated with a specific annotation should match specific naming patterns. | +| Naming | `fieldsShouldNotMatch` | Fields should not match specific naming patterns. | +| Naming | `fieldsShouldMatch` | Fields should match specific naming patterns for specific classes. | +| Naming | `fieldsAnnotatedWithShouldMatch` | Fields annotated with a specific annotation should match specific naming patterns. | +| Naming | `constantsShouldFollowConventions` | Constants should follow naming conventions, except `serialVersionUID`. | +| Naming | `interfacesShouldNotHavePrefixI` | Interfaces should not have the prefix `I`. | ### Logging Rules @@ -268,6 +269,18 @@ Taikai.builder() .check(); ``` +- **Methods Should Not Declare Specific Exception**: Ensure that methods with names matching a specified pattern do not declare a specified exception type. + +```java +Taikai.builder() + .namespace("com.company.yourproject") + .java(java -> java + .methodsShouldNotDeclareException("should*", SpecificException.class) + .methodsShouldNotDeclareException("should*", "com.company.yourproject.SpecificException")) + .build() + .check(); +``` + - **Utility Classes Should Be Final and Have Private Constructor**: Ensure that utility classes with only `static` methods except `main` should be declared as `final` and have `private` constructors to prevent instantiation. ```java diff --git a/src/main/java/com/enofex/taikai/java/JavaConfigurer.java b/src/main/java/com/enofex/taikai/java/JavaConfigurer.java index d850f98..124679e 100644 --- a/src/main/java/com/enofex/taikai/java/JavaConfigurer.java +++ b/src/main/java/com/enofex/taikai/java/JavaConfigurer.java @@ -60,6 +60,33 @@ public JavaConfigurer methodsShouldNotDeclareGenericExceptions(Configuration con .as("Methods should not declare generic Exception or RuntimeException"), configuration)); } + public JavaConfigurer methodsShouldNotDeclareException(String regex, + Class clazz) { + return methodsShouldNotDeclareException(regex, clazz, Configuration.defaultConfiguration()); + } + + public JavaConfigurer methodsShouldNotDeclareException(String regex, + Class clazz, Configuration configuration) { + return addRule(TaikaiRule.of(methods() + .that().haveNameMatching(regex) + .should().notDeclareThrowableOfType(clazz) + .as("Methods have name matching %s should not declare %s".formatted(regex, clazz)), + configuration)); + } + + public JavaConfigurer methodsShouldNotDeclareException(String regex, String typeName) { + return methodsShouldNotDeclareException(regex, typeName, Configuration.defaultConfiguration()); + } + + public JavaConfigurer methodsShouldNotDeclareException(String regex, String typeName, + Configuration configuration) { + return addRule(TaikaiRule.of(methods() + .that().haveNameMatching(regex) + .should().notDeclareThrowableOfType(typeName) + .as("Methods have name matching %s should not declare %s".formatted(regex, typeName)), + configuration)); + } + public JavaConfigurer noUsageOfDeprecatedAPIs() { return noUsageOfDeprecatedAPIs(Configuration.defaultConfiguration()); } diff --git a/src/test/java/com/enofex/taikai/Usage.java b/src/test/java/com/enofex/taikai/Usage.java index 1fbe0c3..57fd5cb 100644 --- a/src/test/java/com/enofex/taikai/Usage.java +++ b/src/test/java/com/enofex/taikai/Usage.java @@ -26,6 +26,7 @@ public static void main(String[] args) { .classesShouldBeAnnotatedWith("regex", DisplayName.class) .classesAnnotatedWithShouldResideInPackage(DisplayName.class, "com.enofex.taikai") .methodsShouldNotDeclareGenericExceptions() + .methodsShouldNotDeclareException("regex", RuntimeException.class) .finalClassesShouldNotHaveProtectedMembers() .utilityClassesShouldBeFinalAndHavePrivateConstructor() .serialVersionUIDFieldsShouldBeStaticFinalLong()