Add ArchUnit architecture checks

This commit adds a new custom build Plugin, the `ArchitecturePlugin`.
This plugin is using ArchUnit to enforce several rules in the main
sources of the project:

* All "package-info" should be `@NullMarked`
* Classes should not import forbidden types (like "reactor.core.support.Assert"
* Java Classes should not import "org.jetbrains.annotations.*" annotations
* Classes should not call "toLowerCase"/"toUpperCase" without a Locale
* There should not be any package tangle

Duplicate rules were removed from checkstyle as a result.
Note, these checks only consider the "main" source sets, so test
fixtures and tests are not considered. Repackaged sources like JavaPoet,
CGLib and ASM are also excluded from the analysis.

Closes gh-34276
This commit is contained in:
Brian Clozel
2025-01-17 17:15:37 +01:00
parent 1ab3d89f10
commit 1bd9848d42
8 changed files with 317 additions and 39 deletions

View File

@@ -7,12 +7,9 @@
<!-- Global: package-info.java -->
<suppress files="(^(?!.+[\\/]src[\\/]main[\\/]java[\\/]).*)|(.*framework-docs.*)" checks="JavadocPackage" />
<suppress files="(^(?!.+[\\/]src[\\/]main[\\/]java[\\/].*package-info\.java))|(.*framework-docs.*)|(.*spring-(context-indexer|instrument|jcl).*)" checks="RegexpSinglelineJava" id="packageLevelNullMarkedAnnotation" />
<!-- Global: tests and test fixtures -->
<suppress files="[\\/]src[\\/](test|testFixtures)[\\/](java|java21)[\\/]" checks="AnnotationLocation|AnnotationUseStyle|AtclauseOrder|AvoidNestedBlocks|FinalClass|HideUtilityClassConstructor|InnerTypeLast|JavadocStyle|JavadocType|JavadocVariable|LeftCurly|MultipleVariableDeclarations|NeedBraces|OneTopLevelClass|OuterTypeFilename|RequireThis|SpringCatch|SpringJavadoc|SpringNoThis"/>
<suppress files="[\\/]src[\\/](test|testFixtures)[\\/](java|java21)[\\/]" checks="RegexpSinglelineJava" id="toLowerCaseWithoutLocale"/>
<suppress files="[\\/]src[\\/](test|testFixtures)[\\/](java|java21)[\\/]" checks="RegexpSinglelineJava" id="toUpperCaseWithoutLocale"/>
<suppress files="[\\/]src[\\/](test|testFixtures)[\\/](java|java21)[\\/]" checks="RegexpSinglelineJava" id="systemOutErrPrint"/>
<suppress files="[\\/]src[\\/](test|testFixtures)[\\/](java|java21)[\\/]org[\\/]springframework[\\/].+(Tests|Suite)" checks="IllegalImport" id="bannedJUnitJupiterImports"/>
<suppress files="[\\/]src[\\/](test|testFixtures)[\\/](java|java21)[\\/]" checks="SpringJUnit5" message="should not be public"/>
@@ -25,7 +22,6 @@
<!-- spring-aop -->
<suppress files="[\\/]src[\\/]main[\\/]java[\\/]org[\\/]aopalliance[\\/]" checks="IllegalImport" id="bannedImports" message="javax"/>
<suppress files="[\\/]src[\\/]main[\\/]java[\\/]org[\\/]aopalliance[\\/]" checks="RegexpSinglelineJava" id="packageLevelNullMarkedAnnotation"/>
<!-- spring-beans -->
<suppress files="TypeMismatchException" checks="MutableException"/>
@@ -37,16 +33,13 @@
<suppress files="RootBeanDefinition" checks="EqualsHashCode"/>
<!-- spring-context -->
<suppress files="[\\/]src[\\/]main[\\/]java[\\/]org[\\/]springframework[\\/]instrument[\\/]" checks="RegexpSinglelineJava" id="packageLevelNullMarkedAnnotation"/>
<suppress files="SpringAtInjectTckTests" checks="IllegalImportCheck" id="bannedJUnit3Imports"/>
<!-- spring-core -->
<suppress files="[\\/]src[\\/]main[\\/]java[\\/]org[\\/]springframework[\\/](asm|cglib|objenesis|javapoet)[\\/]" checks=".*"/>
<suppress files="[\\/]src[\\/]main[\\/]java[\\/]org[\\/]springframework[\\/]aot[\\/]nativex[\\/]" checks="RegexpSinglelineJava" id="packageLevelNullMarkedAnnotation"/>
<suppress files="[\\/]src[\\/]main[\\/]java[\\/]org[\\/]springframework[\\/]aot[\\/]nativex[\\/]feature[\\/]" checks="RegexpSinglelineJava" id="systemOutErrPrint"/>
<suppress files="[\\/]src[\\/]main[\\/]java[\\/]org[\\/]springframework[\\/](core|util)[\\/](SpringProperties|SystemPropertyUtils)" checks="RegexpSinglelineJava" id="systemOutErrPrint"/>
<suppress files="[\\/]src[\\/]main[\\/]java[\\/]org[\\/]springframework[\\/]lang[\\/]" checks="IllegalImport" id="bannedImports" message="javax"/>
<suppress files="[\\/]src[\\/]main[\\/]java[\\/]org[\\/]springframework[\\/]lang[\\/]" checks="RegexpSinglelineJava" id="packageLevelNullMarkedAnnotation" />
<suppress files="[\\/]src[\\/]main[\\/]java[\\/]org[\\/]springframework[\\/]core[\\/]annotation[\\/]" checks="IllegalImport" id="bannedImports" message="javax"/>
<suppress files="[\\/]src[\\/]test[\\/]java[\\/]org[\\/]springframework[\\/]core[\\/]annotation[\\/]" checks="IllegalImport" id="bannedImports" message="javax"/>
<suppress files="ByteArrayEncoder" checks="SpringLambda"/>

View File

@@ -20,16 +20,6 @@
<!-- TreeWalker Checks -->
<module name="com.puppycrawl.tools.checkstyle.TreeWalker">
<!-- Package-level null-safety annotations -->
<module name="RegexpSinglelineJavaCheck">
<property name="id" value="packageLevelNullMarkedAnnotation"/>
<property name="format" value="@NullMarked"/>
<property name="minimum" value="1"/>
<property name="maximum" value="1"/>
<property name="message" value="package-info.java is missing required null-safety annotation @NullMarked."/>
<property name="ignoreComments" value="true"/>
</module>
<!-- Annotations -->
<module name="com.puppycrawl.tools.checkstyle.checks.annotation.AnnotationUseStyleCheck">
<property name="elementStyle" value="compact"/>
@@ -143,11 +133,6 @@
<property name="regexp" value="true"/>
<property name="illegalClasses" value="^org\.hamcrest\..+"/>
</module>
<module name="com.puppycrawl.tools.checkstyle.checks.imports.IllegalImportCheck">
<property name="id" value="bannedJetbrainsAnnotationsImports"/>
<property name="regexp" value="true"/>
<property name="illegalClasses" value="^org\.jetbrains\.annotations\..+"/>
</module>
<!-- Javadoc Comments -->
<module name="com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocTypeCheck">
@@ -220,22 +205,6 @@
<property name="message" value="Please use class literals for primitives and void -- for example, int.class instead of Integer.TYPE."/>
<property name="ignoreComments" value="true"/>
</module>
<module name="com.puppycrawl.tools.checkstyle.checks.regexp.RegexpSinglelineJavaCheck">
<property name="id" value="toLowerCaseWithoutLocale"/>
<property name="format" value="\.toLowerCase\(\)"/>
<property name="maximum" value="0"/>
<property name="message"
value="String.toLowerCase() should be String.toLowerCase(Locale.ROOT)"/>
<property name="ignoreComments" value="true"/>
</module>
<module name="com.puppycrawl.tools.checkstyle.checks.regexp.RegexpSinglelineJavaCheck">
<property name="id" value="toUpperCaseWithoutLocale"/>
<property name="format" value="\.toUpperCase\(\)"/>
<property name="maximum" value="0"/>
<property name="message"
value="String.toUpperCase() should be String.toUpperCase(Locale.ROOT)"/>
<property name="ignoreComments" value="true"/>
</module>
<module name="com.puppycrawl.tools.checkstyle.checks.regexp.RegexpSinglelineJavaCheck">
<property name="id" value="systemOutErrPrint"/>
<property name="format" value="System\.(out|err)\.print"/>