mirror of
https://github.com/PaperMC/Paper
synced 2026-08-20 18:26:12 -04:00
Checkstyle configuration for API (#11859)
This commit is contained in:
parent
9119d1d121
commit
1cd6d5799c
53 changed files with 1799 additions and 71 deletions
203
.checkstyle/checkstyle_base.xml
Normal file
203
.checkstyle/checkstyle_base.xml
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
<!DOCTYPE module PUBLIC
|
||||
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
|
||||
"https://checkstyle.org/dtds/configuration_1_3.dtd">
|
||||
<module name="Checker">
|
||||
<module name="SuppressionFilter">
|
||||
<property name="file" value="${config_loc}/suppressions.xml"/>
|
||||
<property name="optional" value="false"/>
|
||||
</module>
|
||||
|
||||
<!--Single Suppression Filters-->
|
||||
<module name="io.papermc.checkstyle.filters.BetterSuppressionSingleFilter">
|
||||
<!--Suppresses warnings except for common TYPE_USE that should be on the same line-->
|
||||
<property name="checks" value="AnnotationOnSameLine"/>
|
||||
<property name="messageKey" value="annotation\.same\.line"/>
|
||||
<property name="arguments" value="^(?!(${type_use_annotations}))"/>
|
||||
</module>
|
||||
|
||||
<!--Javadoc Comments-->
|
||||
<module name="JavadocPackage"/>
|
||||
<!--Misc-->
|
||||
<module name="NewlineAtEndOfFile"/>
|
||||
<!--Whitespace-->
|
||||
<module name="FileTabCharacter"/>
|
||||
|
||||
<module name="TreeWalker">
|
||||
<module name="SuppressionXpathFilter">
|
||||
<property name="file" value="${config_loc}/xpath-suppressions.xml"/>
|
||||
<property name="optional" value="false"/>
|
||||
</module>
|
||||
<module name="SuppressionCommentFilter"/>
|
||||
|
||||
<!--Annotations-->
|
||||
<module name="AnnotationOnSameLine">
|
||||
<!--matches all annotations, but most are suppressed in above-->
|
||||
<property name="tokens" value="METHOD_DEF"/>
|
||||
</module>
|
||||
<module name="AnnotationUseStyle"/>
|
||||
<module name="MissingDeprecated"/>
|
||||
|
||||
<!--Block Checks-->
|
||||
<module name="AvoidNestedBlocks"/>
|
||||
<module name="EmptyBlock"/>
|
||||
<module name="LeftCurly"/>
|
||||
<module name="RightCurly"/>
|
||||
|
||||
<!--Class Design-->
|
||||
<module name="FinalClass"/>
|
||||
<module name="HideUtilityClassConstructor"/>
|
||||
<module name="InterfaceIsType"/>
|
||||
<module name="OneTopLevelClass"/>
|
||||
<module name="SealedShouldHavePermitsList"/>
|
||||
|
||||
<!--Coding-->
|
||||
<module name="AvoidDoubleBraceInitialization"/>
|
||||
<module name="AvoidNoArgumentSuperConstructorCall"/>
|
||||
<module name="ConstructorsDeclarationGrouping"/>
|
||||
<module name="CovariantEquals"/>
|
||||
<module name="DeclarationOrder"/>
|
||||
<module name="DefaultComesLast"/>
|
||||
<module name="EmptyStatement"/>
|
||||
<module name="EqualsHashCode"/>
|
||||
<module name="FallThrough"/>
|
||||
<!-- Use non-final variables as an identifier of old code for now -->
|
||||
<!--<module name="FinalLocalVariable">
|
||||
<property name="validateEnhancedForLoopVariable" value="true"/>
|
||||
<property name="validateUnnamedVariables" value="true"/>
|
||||
<property name="tokens" value="PARAMETER_DEF,VARIABLE_DEF"/>
|
||||
</module>-->
|
||||
<module name="IllegalToken"/> <!--just labels by default-->
|
||||
<module name="IllegalType"/>
|
||||
<module name="PatternVariableAssignment"/>
|
||||
<module name="RequireThis">
|
||||
<property name="validateOnlyOverlapping" value="false"/>
|
||||
</module>
|
||||
<module name="SimplifyBooleanExpression"/>
|
||||
<module name="SimplifyBooleanReturn"/>
|
||||
<module name="StringLiteralEquality"/>
|
||||
<module name="UnnecessaryNullCheckWithInstanceOf"/>
|
||||
<module name="UnnecessarySemicolonAfterOuterTypeDeclaration"/>
|
||||
<module name="UnnecessarySemicolonAfterTypeMemberDeclaration"/>
|
||||
<module name="UnnecessarySemicolonInEnumeration"/>
|
||||
<module name="UnnecessarySemicolonInTryWithResources"/>
|
||||
<module name="UnusedCatchParameterShouldBeUnnamed"/>
|
||||
<module name="UnusedLocalVariable"/>
|
||||
<module name="WhenShouldBeUsed"/>
|
||||
|
||||
<!--Headers--> <!--N/A-->
|
||||
|
||||
<!--Imports-->
|
||||
<module name="AvoidStarImport"/>
|
||||
<module name="CustomImportOrder">
|
||||
<property name="customImportOrderRules" value="THIRD_PARTY_PACKAGE,STATIC"/>
|
||||
<property name="standardPackageRegExp" value="^$"/>
|
||||
<property name="sortImportsInGroupAlphabetically" value="true"/>
|
||||
</module>
|
||||
<module name="IllegalImport">
|
||||
<property name="regexp" value="true"/>
|
||||
<!--checker-qual nullability-->
|
||||
<property name="illegalClasses" value="org\.checkerframework\.checker\.nullness\.qual\.(Nullable|NonNull|DefaultQualifier|MonotonicNonNull)"/>
|
||||
<!--jetbrains nullability-->
|
||||
<property name="illegalClasses" value="org\.jetbrains\.annotations\.(NotNull|Nullable|NotNullByDefault)"/>
|
||||
<!--javax nullability-->
|
||||
<property name="illegalClasses" value="javax\.annotation\.(Nonnull|Nullable)"/>
|
||||
<message key="import.illegal" value="Illegal nullability annotation - {0}, use JSpecify"/>
|
||||
</module>
|
||||
<module name="RedundantImport"/>
|
||||
<module name="UnusedImports"/>
|
||||
|
||||
<!--Javadoc Comments-->
|
||||
<module name="AtclauseOrder">
|
||||
<property name="tagOrder" value="@param, @return, @throws, @see, @deprecated, @hidden"/>
|
||||
</module>
|
||||
<module name="InvalidJavadocPosition"/>
|
||||
<module name="JavadocBlockTagLocation"/>
|
||||
<module name="JavadocContentLocation"/>
|
||||
<module name="JavadocLeadingAsteriskAlign"/>
|
||||
<module name="JavadocMethod">
|
||||
<!--checks all, but doesn't require. If we have a doc, it should be valid-->
|
||||
<property name="validateThrows" value="true"/>
|
||||
</module>
|
||||
<module name="JavadocMissingLeadingAsterisk"/>
|
||||
<module name="JavadocMissingWhitespaceAfterAsterisk"/>
|
||||
<module name="JavadocStyle"/> <!--checks all, but doesn't require. If we have a doc, it should be valid-->
|
||||
<module name="JavadocTagContinuationIndentation"/>
|
||||
<module name="JavadocType"/> <!--checks all, but doesn't require. If we have a doc, it should be valid-->
|
||||
<module name="NonEmptyAtclauseDescription"/>
|
||||
<module name="RequireEmptyLineBeforeBlockTagGroup"/>
|
||||
|
||||
<!--Metrics--> <!--N/A-->
|
||||
|
||||
<!--Miscellaneous-->
|
||||
<module name="ArrayTypeStyle"/>
|
||||
<module name="AvoidEscapedUnicodeCharacters"/>
|
||||
<module name="CommentsIndentation"/>
|
||||
<!-- Overlaps with the configured FinalLocalVariable, but is more strict -->
|
||||
<!--<module name="FinalParameters">
|
||||
<property name="tokens" value="METHOD_DEF,CTOR_DEF,LITERAL_CATCH,FOR_EACH_CLAUSE,PATTERN_VARIABLE_DEF"/>
|
||||
</module>-->
|
||||
|
||||
<module name="NoCodeInFile"/>
|
||||
<module name="OuterTypeFilename"/>
|
||||
<module name="UpperEll"/>
|
||||
|
||||
<!--Modifiers-->
|
||||
<module name="ModifierOrder"/>
|
||||
<module name="RedundantModifier"/>
|
||||
|
||||
<!--Naming Conventions-->
|
||||
<module name="AbbreviationAsWordInName">
|
||||
<property name="allowedAbbreviations" value="JSON,UUID"/>
|
||||
<property name="ignoreFinal" value="false"/>
|
||||
</module>
|
||||
<module name="ClassTypeParameterName"/>
|
||||
<module name="ConstantName"/>
|
||||
<module name="IllegalIdentifierName"/>
|
||||
<module name="InterfaceTypeParameterName"/>
|
||||
<module name="LambdaParameterName"/>
|
||||
<module name="LocalFinalVariableName"/>
|
||||
<module name="LocalVariableName"/>
|
||||
<module name="MemberName"/>
|
||||
<module name="MethodName"/>
|
||||
<module name="MethodTypeParameterName">
|
||||
<property name="format" value="^[A-Z]+$"/>
|
||||
</module>
|
||||
<module name="PackageName">
|
||||
<property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/>
|
||||
</module>
|
||||
<module name="ParameterName"/>
|
||||
<module name="PatternVariableName"/>
|
||||
<module name="RecordComponentName"/>
|
||||
<module name="RecordTypeParameterName"/>
|
||||
<module name="StaticVariableName"/>
|
||||
<module name="TypeName"/>
|
||||
|
||||
<!--Regexp--> <!--N/A-->
|
||||
<!--Size Violations--> <!--N/A-->
|
||||
|
||||
<!--Whitespace-->
|
||||
<module name="EmptyForInitializerPad"/>
|
||||
<module name="EmptyForIteratorPad"/>
|
||||
<module name="EmptyLineSeparator">
|
||||
<property name="allowNoEmptyLineBetweenFields" value="true"/>
|
||||
<property name="tokens" value="IMPORT,STATIC_IMPORT,CLASS_DEF,INTERFACE_DEF,ENUM_DEF,STATIC_INIT,INSTANCE_INIT,METHOD_DEF,CTOR_DEF,VARIABLE_DEF,RECORD_DEF,COMPACT_CTOR_DEF"/>
|
||||
</module>
|
||||
<module name="GenericWhitespace"/>
|
||||
<module name="MethodParamPad"/>
|
||||
<module name="NoLineWrap"/> <!--just imports and packages-->
|
||||
<module name="NoWhitespaceAfter"/>
|
||||
<module name="NoWhitespaceBefore"/>
|
||||
<module name="NoWhitespaceBeforeCaseDefaultColon"/>
|
||||
<module name="ParenPad"/>
|
||||
<module name="SingleSpaceSeparator"/>
|
||||
<module name="TypecastParenPad"/>
|
||||
<module name="WhitespaceAfter"/>
|
||||
<module name="WhitespaceAround"/>
|
||||
|
||||
<!--Custom-->
|
||||
<module name="JavadocAlignParameterDescription"/>
|
||||
<module name="NullabilityAnnotations"/>
|
||||
<module name="RedundantNullability"/>
|
||||
<module name="UnnecessaryFullyQualifiedImport"/>
|
||||
</module>
|
||||
</module>
|
||||
7
.checkstyle/suppressions.xml
Normal file
7
.checkstyle/suppressions.xml
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<!DOCTYPE suppressions PUBLIC
|
||||
"-//Checkstyle//DTD SuppressionFilter Configuration 1.2//EN"
|
||||
"https://checkstyle.org/dtds/suppressions_1_2.dtd">
|
||||
<suppressions>
|
||||
<suppress checks="MissingJavadoc.*" files="src[\\/]test[\\/]java[^\\/]*[\\/].*"/>
|
||||
<suppress checks="JavadocPackage" files="src[\\/]test[\\/]java[^\\/]*[\\/].*"/>
|
||||
</suppressions>
|
||||
10
.checkstyle/type_use_annotations.txt
Normal file
10
.checkstyle/type_use_annotations.txt
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
NonNull
|
||||
NotNull
|
||||
Nullable
|
||||
UnknownNullability
|
||||
Unmodifiable
|
||||
UnmodifiableView
|
||||
Range
|
||||
Positive
|
||||
NonNegative
|
||||
IntRange
|
||||
17
.checkstyle/xpath-suppressions.xml
Normal file
17
.checkstyle/xpath-suppressions.xml
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<!DOCTYPE suppressions PUBLIC
|
||||
"-//Checkstyle//DTD SuppressionXpathFilter Experimental Configuration 1.2//EN"
|
||||
"https://checkstyle.org/dtds/suppressions_1_2_xpath_experimental.dtd">
|
||||
<suppressions>
|
||||
<!--ignores all checks for @Deprecated(forRemoval=*) children-->
|
||||
<suppress-xpath checks=".*" query="//*[MODIFIERS/ANNOTATION[(@text = 'Deprecated' or *[@text = 'Deprecated'] and ANNOTATION_MEMBER_VALUE_PAIR/IDENT[@text='forRemoval'])]]/descendant-or-self::node()"/>
|
||||
<!--special case to ignore extra checks outside the root CLASS_DEF if the root CLASS_DEF is marked as deprecated for removal-->
|
||||
<suppress-xpath checks=".*" query="/COMPILATION_UNIT[CLASS_DEF/MODIFIERS/ANNOTATION[IDENT[@text = 'Deprecated'] and ANNOTATION_MEMBER_VALUE_PAIR/IDENT[@text='forRemoval']]]/descendant-or-self::node()"/>
|
||||
<!--ignores MissingJavadoc for ApiStatus.Internal children-->
|
||||
<suppress-xpath checks="MissingJavadoc(Method|Type)" query="//*[MODIFIERS/ANNOTATION/DOT[@text = 'Internal' or *[@text = 'Internal']]]/descendant-or-self::node()"/>
|
||||
|
||||
<!--skip for private classes-->
|
||||
<suppress-xpath checks="FinalClass" query="//CLASS_DEF[MODIFIERS/LITERAL_PRIVATE]"/>
|
||||
|
||||
<!--skip for classes inside of methods-->
|
||||
<suppress-xpath checks="HideUtilityClassConstructor|MissingJavadoc(Method|Type)" query="//CLASS_DEF[ancestor::METHOD_DEF]"/>
|
||||
</suppressions>
|
||||
|
|
@ -10,6 +10,7 @@ ij_any_block_comment_add_space = false
|
|||
ij_any_block_comment_at_first_column = false
|
||||
ij_any_line_comment_at_first_column = false
|
||||
ij_any_line_comment_add_space = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.tiny]
|
||||
indent_style = tab
|
||||
|
|
@ -20,7 +21,7 @@ end_of_line = crlf
|
|||
[*.yml]
|
||||
indent_size = 2
|
||||
|
||||
[*.patch]
|
||||
[{*.patch,*.md}]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.java]
|
||||
|
|
@ -29,12 +30,26 @@ ij_java_class_count_to_use_import_on_demand = 999999
|
|||
ij_java_insert_inner_class_imports = false
|
||||
ij_java_names_count_to_use_import_on_demand = 999999
|
||||
ij_java_imports_layout = *, |, $*
|
||||
ij_java_layout_static_imports_separately = true
|
||||
ij_java_generate_final_locals = true
|
||||
ij_java_generate_final_parameters = true
|
||||
ij_java_method_parameters_new_line_after_left_paren = true
|
||||
ij_java_method_parameters_right_paren_on_new_line = true
|
||||
ij_java_use_fq_class_names = false
|
||||
ij_java_class_names_in_javadoc = 1
|
||||
# javadoc
|
||||
ij_java_doc_add_blank_line_after_description = true
|
||||
ij_java_doc_add_blank_line_after_param_comments = false
|
||||
ij_java_doc_add_blank_line_after_return = false
|
||||
ij_java_doc_add_p_tag_on_empty_lines = true
|
||||
ij_java_doc_align_exception_comments = true
|
||||
ij_java_doc_align_param_comments = true
|
||||
ij_java_doc_do_not_wrap_if_one_line = false
|
||||
ij_java_doc_enable_formatting = true
|
||||
ij_java_doc_enable_leading_asterisks = true
|
||||
ij_java_doc_indent_on_continuation = true
|
||||
ij_java_doc_param_description_on_new_line = false
|
||||
ij_java_doc_use_throws_not_exception_tag = true
|
||||
|
||||
[paper-server/src/minecraft/java/**/*.java]
|
||||
ij_java_use_fq_class_names = true
|
||||
|
|
|
|||
7
.github/workflows/build.yml
vendored
7
.github/workflows/build.yml
vendored
|
|
@ -111,6 +111,13 @@ jobs:
|
|||
path: |
|
||||
**/build/test-results/test/TEST-*.xml
|
||||
|
||||
- name: Publish Checkstyle Report
|
||||
uses: Juuxel/publish-checkstyle-report@v3
|
||||
if: failure() || success()
|
||||
with:
|
||||
reports: |
|
||||
*/build/reports/checkstyle/*.xml
|
||||
|
||||
- name: Create Paperclip Jar
|
||||
if: fromJSON(steps.determine.outputs.result).action == 'paperclip'
|
||||
run: ./gradlew createPaperclipJar --stacktrace
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import io.papermc.paperweight.checkstyle.PaperCheckstyleExt
|
||||
import io.papermc.paperweight.checkstyle.tasks.PaperCheckstyleTask
|
||||
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
|
||||
import org.gradle.api.tasks.testing.logging.TestLogEvent
|
||||
|
||||
|
|
@ -6,14 +8,39 @@ plugins {
|
|||
}
|
||||
|
||||
subprojects {
|
||||
apply(plugin = "java-library")
|
||||
apply(plugin = "maven-publish")
|
||||
apply {
|
||||
plugin("java-library")
|
||||
plugin("maven-publish")
|
||||
}
|
||||
|
||||
extensions.configure<JavaPluginExtension> {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(25)
|
||||
}
|
||||
}
|
||||
|
||||
val tempDisabled = setOf("paper-server", "paper-generator", "test-plugin")
|
||||
|
||||
if (name !in tempDisabled) {
|
||||
apply { plugin("io.papermc.paperweight.paper-checkstyle") }
|
||||
extensions.configure<PaperCheckstyleExt> {
|
||||
typeUseAnnotationsFile.set(rootProject.layout.projectDirectory.file(".checkstyle/type_use_annotations.txt"))
|
||||
}
|
||||
|
||||
/*tasks.withType<PaperCheckstyleTask>().configureEach {
|
||||
configDirectory = rootProject.layout.projectDirectory.dir(".checkstyle")
|
||||
// configFile = layout.projectDirectory.file(".checkstyle/checkstyle.xml").asFile // use the base file if not overwritten
|
||||
maxHeapSize = "2g"
|
||||
reports {
|
||||
xml.required = true
|
||||
html.required = true
|
||||
}
|
||||
}*/
|
||||
|
||||
dependencies {
|
||||
"checkstyle"(project(":paper-checkstyle"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val paperMavenPublicUrl = "https://repo.papermc.io/repository/maven-public/"
|
||||
|
|
|
|||
26
paper-api/.checkstyle/checkstyle.xml
Normal file
26
paper-api/.checkstyle/checkstyle.xml
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
<!DOCTYPE module PUBLIC
|
||||
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
|
||||
"https://checkstyle.org/dtds/configuration_1_3.dtd">
|
||||
<module name="Checker">
|
||||
<!--Single Suppression Filters-->
|
||||
<module name="io.papermc.checkstyle.filters.BetterSuppressionSingleFilter">
|
||||
<property name="checks" value="Javadoc(Method|Type)"/>
|
||||
<property name="messageKey" value="javadoc\.unknownTag"/>
|
||||
<!--[A-Z].* check is for java annotations inside code blocks inside Javadocs-->
|
||||
<property name="arguments" value="(${custom_javadoc_tags}|[A-Z].*)"/>
|
||||
</module>
|
||||
<module name="TreeWalker">
|
||||
<module name="JavadocVariable">
|
||||
<!--requires on public fields-->
|
||||
<property name="accessModifiers" value="public"/>
|
||||
</module>
|
||||
<module name="MissingJavadocMethod">
|
||||
<property name="allowedAnnotations" value="Override, ApiStatus.Internal"/>
|
||||
<property name="ignoreMethodNamesRegex" value="^getHandlerList$"/>
|
||||
</module>
|
||||
<module name="MissingJavadocPackage"/>
|
||||
<module name="MissingJavadocType">
|
||||
<property name="skipAnnotations" value="Generated, ApiStatus.Internal" />
|
||||
</module>
|
||||
</module>
|
||||
</module>
|
||||
201
paper-api/.checkstyle/ignored_directories.txt
Normal file
201
paper-api/.checkstyle/ignored_directories.txt
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
paper-api/src/generated/java/com/destroystokyo/paper/entity/ai/
|
||||
paper-api/src/generated/java/io/papermc/paper/registry/keys/
|
||||
paper-api/src/generated/java/io/papermc/paper/registry/keys/tags/
|
||||
paper-api/src/main/java/co/aikar/timings/
|
||||
paper-api/src/main/java/co/aikar/util/
|
||||
paper-api/src/main/java/com/destroystokyo/paper/
|
||||
paper-api/src/main/java/com/destroystokyo/paper/block/
|
||||
paper-api/src/main/java/com/destroystokyo/paper/brigadier/
|
||||
paper-api/src/main/java/com/destroystokyo/paper/entity/
|
||||
paper-api/src/main/java/com/destroystokyo/paper/entity/ai/
|
||||
paper-api/src/main/java/com/destroystokyo/paper/entity/villager/
|
||||
paper-api/src/main/java/com/destroystokyo/paper/event/block/
|
||||
paper-api/src/main/java/com/destroystokyo/paper/event/brigadier/
|
||||
paper-api/src/main/java/com/destroystokyo/paper/event/entity/
|
||||
paper-api/src/main/java/com/destroystokyo/paper/event/inventory/
|
||||
paper-api/src/main/java/com/destroystokyo/paper/event/player/
|
||||
paper-api/src/main/java/com/destroystokyo/paper/event/profile/
|
||||
paper-api/src/main/java/com/destroystokyo/paper/event/server/
|
||||
paper-api/src/main/java/com/destroystokyo/paper/exception/
|
||||
paper-api/src/main/java/com/destroystokyo/paper/inventory/meta/
|
||||
paper-api/src/main/java/com/destroystokyo/paper/loottable/
|
||||
paper-api/src/main/java/com/destroystokyo/paper/network/
|
||||
paper-api/src/main/java/com/destroystokyo/paper/profile/
|
||||
paper-api/src/main/java/com/destroystokyo/paper/util/
|
||||
paper-api/src/main/java/com/destroystokyo/paper/utils/
|
||||
paper-api/src/main/java/io/papermc/paper/advancement/
|
||||
paper-api/src/main/java/io/papermc/paper/annotation/
|
||||
paper-api/src/main/java/io/papermc/paper/ban/
|
||||
paper-api/src/main/java/io/papermc/paper/block/
|
||||
paper-api/src/main/java/io/papermc/paper/block/bed/
|
||||
paper-api/src/main/java/io/papermc/paper/block/fluid/
|
||||
paper-api/src/main/java/io/papermc/paper/block/fluid/type/
|
||||
paper-api/src/main/java/io/papermc/paper/brigadier/
|
||||
paper-api/src/main/java/io/papermc/paper/chat/
|
||||
paper-api/src/main/java/io/papermc/paper/command/
|
||||
paper-api/src/main/java/io/papermc/paper/command/brigadier/
|
||||
paper-api/src/main/java/io/papermc/paper/command/brigadier/argument/
|
||||
paper-api/src/main/java/io/papermc/paper/command/brigadier/argument/position/
|
||||
paper-api/src/main/java/io/papermc/paper/command/brigadier/argument/predicate/
|
||||
paper-api/src/main/java/io/papermc/paper/command/brigadier/argument/range/
|
||||
paper-api/src/main/java/io/papermc/paper/command/brigadier/argument/resolvers/
|
||||
paper-api/src/main/java/io/papermc/paper/command/brigadier/argument/resolvers/selector/
|
||||
paper-api/src/main/java/io/papermc/paper/configuration/
|
||||
paper-api/src/main/java/io/papermc/paper/connection/
|
||||
paper-api/src/main/java/io/papermc/paper/datacomponent/
|
||||
paper-api/src/main/java/io/papermc/paper/datacomponent/item/
|
||||
paper-api/src/main/java/io/papermc/paper/datacomponent/item/attribute/
|
||||
paper-api/src/main/java/io/papermc/paper/datacomponent/item/blocksattacks/
|
||||
paper-api/src/main/java/io/papermc/paper/datacomponent/item/consumable/
|
||||
paper-api/src/main/java/io/papermc/paper/datapack/
|
||||
paper-api/src/main/java/io/papermc/paper/dialog/
|
||||
paper-api/src/main/java/io/papermc/paper/enchantments/
|
||||
paper-api/src/main/java/io/papermc/paper/entity/
|
||||
paper-api/src/main/java/io/papermc/paper/entity/poi/
|
||||
paper-api/src/main/java/io/papermc/paper/event/block/
|
||||
paper-api/src/main/java/io/papermc/paper/event/connection/
|
||||
paper-api/src/main/java/io/papermc/paper/event/connection/configuration/
|
||||
paper-api/src/main/java/io/papermc/paper/event/entity/
|
||||
paper-api/src/main/java/io/papermc/paper/event/executor/
|
||||
paper-api/src/main/java/io/papermc/paper/event/inventory/
|
||||
paper-api/src/main/java/io/papermc/paper/event/packet/
|
||||
paper-api/src/main/java/io/papermc/paper/event/player/
|
||||
paper-api/src/main/java/io/papermc/paper/event/server/
|
||||
paper-api/src/main/java/io/papermc/paper/event/world/
|
||||
paper-api/src/main/java/io/papermc/paper/event/world/border/
|
||||
paper-api/src/main/java/io/papermc/paper/generated/
|
||||
paper-api/src/main/java/io/papermc/paper/inventory/
|
||||
paper-api/src/main/java/io/papermc/paper/inventory/tooltip/
|
||||
paper-api/src/main/java/io/papermc/paper/item/
|
||||
paper-api/src/main/java/io/papermc/paper/persistence/
|
||||
paper-api/src/main/java/io/papermc/paper/plugin/
|
||||
paper-api/src/main/java/io/papermc/paper/plugin/bootstrap/
|
||||
paper-api/src/main/java/io/papermc/paper/plugin/configuration/
|
||||
paper-api/src/main/java/io/papermc/paper/plugin/lifecycle/event/
|
||||
paper-api/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/
|
||||
paper-api/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/configuration/
|
||||
paper-api/src/main/java/io/papermc/paper/plugin/lifecycle/event/registrar/
|
||||
paper-api/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/
|
||||
paper-api/src/main/java/io/papermc/paper/plugin/loader/
|
||||
paper-api/src/main/java/io/papermc/paper/plugin/loader/library/
|
||||
paper-api/src/main/java/io/papermc/paper/plugin/loader/library/impl/
|
||||
paper-api/src/main/java/io/papermc/paper/plugin/provider/classloader/
|
||||
paper-api/src/main/java/io/papermc/paper/plugin/provider/entrypoint/
|
||||
paper-api/src/main/java/io/papermc/paper/plugin/provider/util/
|
||||
paper-api/src/main/java/io/papermc/paper/potion/
|
||||
paper-api/src/main/java/io/papermc/paper/raytracing/
|
||||
paper-api/src/main/java/io/papermc/paper/registry/
|
||||
paper-api/src/main/java/io/papermc/paper/registry/data/
|
||||
paper-api/src/main/java/io/papermc/paper/registry/data/client/
|
||||
paper-api/src/main/java/io/papermc/paper/registry/data/dialog/
|
||||
paper-api/src/main/java/io/papermc/paper/registry/data/dialog/action/
|
||||
paper-api/src/main/java/io/papermc/paper/registry/data/dialog/body/
|
||||
paper-api/src/main/java/io/papermc/paper/registry/data/dialog/input/
|
||||
paper-api/src/main/java/io/papermc/paper/registry/data/dialog/type/
|
||||
paper-api/src/main/java/io/papermc/paper/registry/event/
|
||||
paper-api/src/main/java/io/papermc/paper/registry/event/type/
|
||||
paper-api/src/main/java/io/papermc/paper/registry/holder/
|
||||
paper-api/src/main/java/io/papermc/paper/registry/set/
|
||||
paper-api/src/main/java/io/papermc/paper/registry/tag/
|
||||
paper-api/src/main/java/io/papermc/paper/scoreboard/numbers/
|
||||
paper-api/src/main/java/io/papermc/paper/tag/
|
||||
paper-api/src/main/java/io/papermc/paper/text/
|
||||
paper-api/src/main/java/io/papermc/paper/threadedregions/
|
||||
paper-api/src/main/java/io/papermc/paper/threadedregions/scheduler/
|
||||
paper-api/src/main/java/io/papermc/paper/util/
|
||||
paper-api/src/main/java/io/papermc/paper/world/
|
||||
paper-api/src/main/java/io/papermc/paper/world/damagesource/
|
||||
paper-api/src/main/java/io/papermc/paper/world/flag/
|
||||
paper-api/src/main/java/org/bukkit/
|
||||
paper-api/src/main/java/org/bukkit/advancement/
|
||||
paper-api/src/main/java/org/bukkit/attribute/
|
||||
paper-api/src/main/java/org/bukkit/ban/
|
||||
paper-api/src/main/java/org/bukkit/block/
|
||||
paper-api/src/main/java/org/bukkit/block/banner/
|
||||
paper-api/src/main/java/org/bukkit/block/data/
|
||||
paper-api/src/main/java/org/bukkit/block/data/type/
|
||||
paper-api/src/main/java/org/bukkit/block/sign/
|
||||
paper-api/src/main/java/org/bukkit/block/spawner/
|
||||
paper-api/src/main/java/org/bukkit/block/structure/
|
||||
paper-api/src/main/java/org/bukkit/boss/
|
||||
paper-api/src/main/java/org/bukkit/command/
|
||||
paper-api/src/main/java/org/bukkit/command/defaults/
|
||||
paper-api/src/main/java/org/bukkit/configuration/
|
||||
paper-api/src/main/java/org/bukkit/configuration/file/
|
||||
paper-api/src/main/java/org/bukkit/configuration/serialization/
|
||||
paper-api/src/main/java/org/bukkit/conversations/
|
||||
paper-api/src/main/java/org/bukkit/damage/
|
||||
paper-api/src/main/java/org/bukkit/enchantments/
|
||||
paper-api/src/main/java/org/bukkit/entity/
|
||||
paper-api/src/main/java/org/bukkit/entity/boat/
|
||||
paper-api/src/main/java/org/bukkit/entity/memory/
|
||||
paper-api/src/main/java/org/bukkit/entity/minecart/
|
||||
paper-api/src/main/java/org/bukkit/event/
|
||||
paper-api/src/main/java/org/bukkit/event/block/
|
||||
paper-api/src/main/java/org/bukkit/event/command/
|
||||
paper-api/src/main/java/org/bukkit/event/enchantment/
|
||||
paper-api/src/main/java/org/bukkit/event/entity/
|
||||
paper-api/src/main/java/org/bukkit/event/hanging/
|
||||
paper-api/src/main/java/org/bukkit/event/inventory/
|
||||
paper-api/src/main/java/org/bukkit/event/player/
|
||||
paper-api/src/main/java/org/bukkit/event/raid/
|
||||
paper-api/src/main/java/org/bukkit/event/server/
|
||||
paper-api/src/main/java/org/bukkit/event/vehicle/
|
||||
paper-api/src/main/java/org/bukkit/event/weather/
|
||||
paper-api/src/main/java/org/bukkit/event/world/
|
||||
paper-api/src/main/java/org/bukkit/generator/
|
||||
paper-api/src/main/java/org/bukkit/generator/structure/
|
||||
paper-api/src/main/java/org/bukkit/help/
|
||||
paper-api/src/main/java/org/bukkit/inventory/
|
||||
paper-api/src/main/java/org/bukkit/inventory/meta/
|
||||
paper-api/src/main/java/org/bukkit/inventory/meta/components/
|
||||
paper-api/src/main/java/org/bukkit/inventory/meta/tags/
|
||||
paper-api/src/main/java/org/bukkit/inventory/meta/trim/
|
||||
paper-api/src/main/java/org/bukkit/inventory/recipe/
|
||||
paper-api/src/main/java/org/bukkit/inventory/view/
|
||||
paper-api/src/main/java/org/bukkit/inventory/view/builder/
|
||||
paper-api/src/main/java/org/bukkit/loot/
|
||||
paper-api/src/main/java/org/bukkit/map/
|
||||
paper-api/src/main/java/org/bukkit/material/
|
||||
paper-api/src/main/java/org/bukkit/material/types/
|
||||
paper-api/src/main/java/org/bukkit/metadata/
|
||||
paper-api/src/main/java/org/bukkit/packs/
|
||||
paper-api/src/main/java/org/bukkit/permissions/
|
||||
paper-api/src/main/java/org/bukkit/persistence/
|
||||
paper-api/src/main/java/org/bukkit/plugin/
|
||||
paper-api/src/main/java/org/bukkit/plugin/java/
|
||||
paper-api/src/main/java/org/bukkit/plugin/messaging/
|
||||
paper-api/src/main/java/org/bukkit/potion/
|
||||
paper-api/src/main/java/org/bukkit/profile/
|
||||
paper-api/src/main/java/org/bukkit/projectiles/
|
||||
paper-api/src/main/java/org/bukkit/scheduler/
|
||||
paper-api/src/main/java/org/bukkit/scoreboard/
|
||||
paper-api/src/main/java/org/bukkit/spawner/
|
||||
paper-api/src/main/java/org/bukkit/structure/
|
||||
paper-api/src/main/java/org/bukkit/tag/
|
||||
paper-api/src/main/java/org/bukkit/util/
|
||||
paper-api/src/main/java/org/bukkit/util/io/
|
||||
paper-api/src/main/java/org/bukkit/util/noise/
|
||||
paper-api/src/main/java/org/bukkit/util/permissions/
|
||||
paper-api/src/main/java/org/spigotmc/
|
||||
paper-api/src/main/java/org/spigotmc/event/player/
|
||||
paper-api/src/test/java/com/destroystokyo/paper/
|
||||
paper-api/src/test/java/io/papermc/paper/adventure/
|
||||
paper-api/src/test/java/io/papermc/paper/registry/
|
||||
paper-api/src/test/java/io/papermc/paper/testing/
|
||||
paper-api/src/test/java/io/papermc/paper/util/
|
||||
paper-api/src/test/java/org/bukkit/
|
||||
paper-api/src/test/java/org/bukkit/configuration/
|
||||
paper-api/src/test/java/org/bukkit/configuration/file/
|
||||
paper-api/src/test/java/org/bukkit/conversations/
|
||||
paper-api/src/test/java/org/bukkit/event/
|
||||
paper-api/src/test/java/org/bukkit/materials/
|
||||
paper-api/src/test/java/org/bukkit/metadata/
|
||||
paper-api/src/test/java/org/bukkit/plugin/
|
||||
paper-api/src/test/java/org/bukkit/plugin/messaging/
|
||||
paper-api/src/test/java/org/bukkit/scoreboard/
|
||||
paper-api/src/test/java/org/bukkit/support/
|
||||
paper-api/src/test/java/org/bukkit/support/provider/
|
||||
paper-api/src/test/java/org/bukkit/support/test/
|
||||
paper-api/src/test/java/org/bukkit/util/
|
||||
paper-api/src/test/java/org/bukkit/util/io/
|
||||
|
|
@ -1,9 +1,11 @@
|
|||
import io.papermc.paperweight.checkstyle.JavadocTag
|
||||
import paper.libs.com.google.gson.Gson
|
||||
|
||||
plugins {
|
||||
`java-library`
|
||||
`maven-publish`
|
||||
idea
|
||||
id("io.papermc.paperweight.paper-checkstyle")
|
||||
}
|
||||
|
||||
java {
|
||||
|
|
@ -11,6 +13,15 @@ java {
|
|||
withJavadocJar()
|
||||
}
|
||||
|
||||
val projectCustomJavadocTags = setOf(
|
||||
JavadocTag("apiNote", "a", "API Note:"),
|
||||
)
|
||||
|
||||
paperCheckstyle {
|
||||
customJavadocTags = projectCustomJavadocTags
|
||||
directoriesToSkipFile = layout.projectDirectory.file(".checkstyle/ignored_directories.txt")
|
||||
}
|
||||
|
||||
val annotationsVersion = "26.0.2"
|
||||
val adventureVersion = "5.2.0"
|
||||
val bungeeCordChatVersion = "1.21-R0.2-deprecated+build.21"
|
||||
|
|
@ -92,6 +103,9 @@ dependencies {
|
|||
testImplementation("org.ow2.asm:asm-tree:9.9.1")
|
||||
mockitoAgent("org.mockito:mockito-core:5.22.0") { isTransitive = false } // configure mockito agent that is needed in newer java versions
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher:6.0.3")
|
||||
|
||||
// checkstyle
|
||||
checkstyle(project(":paper-checkstyle"))
|
||||
}
|
||||
|
||||
val generatedDir: java.nio.file.Path = layout.projectDirectory.dir("src/generated/java").asFile.toPath()
|
||||
|
|
@ -201,7 +215,7 @@ tasks.withType<Javadoc>().configureEach {
|
|||
"https://logging.apache.org/log4j/2.x/javadoc/log4j-api/",
|
||||
"https://www.javadocs.dev/org.apache.maven.resolver/maven-resolver-api/1.7.3",
|
||||
)
|
||||
options.tags("apiNote:a:API Note:")
|
||||
options.tags(projectCustomJavadocTags.map { it.toOptionString() })
|
||||
|
||||
inputs.files(javadocSourcepath).ignoreEmptyDirectories().withPropertyName(javadocSourcepath.name + "-configuration")
|
||||
val javadocSourcepathElements = javadocSourcepath.elements
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ import org.bukkit.plugin.Plugin;
|
|||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
|
|
@ -45,7 +44,6 @@ import org.jspecify.annotations.Nullable;
|
|||
* cause issues when called under unexpected circumstances.
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
@NullMarked
|
||||
public interface InternalAPIBridge {
|
||||
|
||||
/**
|
||||
|
|
@ -55,6 +53,7 @@ public interface InternalAPIBridge {
|
|||
*/
|
||||
static InternalAPIBridge get() {
|
||||
class Holder {
|
||||
|
||||
public static final InternalAPIBridge INSTANCE = Services.service(InternalAPIBridge.class).orElseThrow();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,12 +6,10 @@ import java.util.OptionalInt;
|
|||
import net.kyori.adventure.key.Key;
|
||||
import net.kyori.adventure.util.Services;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
/**
|
||||
* Information about the current server build.
|
||||
*/
|
||||
@NullMarked
|
||||
@ApiStatus.NonExtendable
|
||||
public interface ServerBuildInfo {
|
||||
/**
|
||||
|
|
@ -30,6 +28,7 @@ public interface ServerBuildInfo {
|
|||
static final Optional<ServerBuildInfo> INSTANCE = Services.service(ServerBuildInfo.class);
|
||||
}
|
||||
//</editor-fold>
|
||||
|
||||
return Holder.INSTANCE.orElseThrow();
|
||||
}
|
||||
|
||||
|
|
@ -46,7 +45,7 @@ public interface ServerBuildInfo {
|
|||
* @param brandId the brand to check (e.g. "papermc:folia")
|
||||
* @return {@code true} if the server supports the specified brand
|
||||
*/
|
||||
boolean isBrandCompatible(final Key brandId);
|
||||
boolean isBrandCompatible(Key brandId);
|
||||
|
||||
/**
|
||||
* Gets the brand name of the server.
|
||||
|
|
@ -103,7 +102,7 @@ public interface ServerBuildInfo {
|
|||
* @param representation the type of representation
|
||||
* @return a string
|
||||
*/
|
||||
String asString(final StringRepresentation representation);
|
||||
String asString(StringRepresentation representation);
|
||||
|
||||
/**
|
||||
* String representation types.
|
||||
|
|
|
|||
|
|
@ -1,11 +1,8 @@
|
|||
package io.papermc.paper.math;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
/**
|
||||
* Represents an angle that can be relative or absolute.
|
||||
*/
|
||||
@NullMarked
|
||||
public sealed interface Angle permits AngleImpl {
|
||||
|
||||
/**
|
||||
|
|
@ -15,7 +12,7 @@ public sealed interface Angle permits AngleImpl {
|
|||
* @param degrees the angle value, measured in degrees
|
||||
* @return a new {@code Angle} instance with the specified value
|
||||
*/
|
||||
static Angle absolute(float degrees) {
|
||||
static Angle absolute(final float degrees) {
|
||||
return new AngleImpl(degrees, false);
|
||||
}
|
||||
|
||||
|
|
@ -26,7 +23,7 @@ public sealed interface Angle permits AngleImpl {
|
|||
* @param degrees the angle value, measured in degrees
|
||||
* @return a new {@code Angle} instance with the specified value
|
||||
*/
|
||||
static Angle relative(float degrees) {
|
||||
static Angle relative(final float degrees) {
|
||||
return new AngleImpl(degrees, true);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import org.bukkit.Axis;
|
|||
import org.bukkit.block.BlockFace;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
/**
|
||||
* A position represented with integers.
|
||||
|
|
@ -14,7 +13,6 @@ import org.jspecify.annotations.NullMarked;
|
|||
* @see FinePosition
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
@NullMarked
|
||||
public interface BlockPosition extends Position {
|
||||
|
||||
@Override
|
||||
|
|
@ -73,7 +71,7 @@ public interface BlockPosition extends Position {
|
|||
* multiplied by the amount.
|
||||
*
|
||||
* @param blockFace the block face to offset towards
|
||||
* @param amount the number of times to move in that direction
|
||||
* @param amount the number of times to move in that direction
|
||||
* @return the offset block position
|
||||
*/
|
||||
@Contract(pure = true)
|
||||
|
|
@ -85,7 +83,7 @@ public interface BlockPosition extends Position {
|
|||
* Returns a block position offset by the amount along
|
||||
* the specified axis.
|
||||
*
|
||||
* @param axis the axis to offset along
|
||||
* @param axis the axis to offset along
|
||||
* @param amount the amount to offset along that axis
|
||||
* @return the offset block position
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package io.papermc.paper.math;
|
|||
|
||||
import org.bukkit.util.NumberConversions;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
/**
|
||||
* A position represented with doubles.
|
||||
|
|
@ -12,7 +11,6 @@ import org.jspecify.annotations.NullMarked;
|
|||
* @see BlockPosition
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
@NullMarked
|
||||
public interface FinePosition extends Position {
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import org.bukkit.World;
|
|||
import org.bukkit.util.Vector;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
/**
|
||||
* Common interface for {@link FinePosition} and {@link BlockPosition}.
|
||||
|
|
@ -13,63 +12,68 @@ import org.jspecify.annotations.NullMarked;
|
|||
* <b>May see breaking changes until Experimental annotation is removed.</b>
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
@NullMarked
|
||||
public interface Position {
|
||||
|
||||
/**
|
||||
* A fine position at the origin (0, 0, 0).
|
||||
*/
|
||||
FinePosition FINE_ZERO = new FinePositionImpl(0, 0, 0);
|
||||
/**
|
||||
* A block position at the origin (0, 0, 0).
|
||||
*/
|
||||
BlockPosition BLOCK_ZERO = new BlockPositionImpl(0, 0, 0);
|
||||
|
||||
/**
|
||||
* Gets the block x value for this position
|
||||
* Gets the block x value for this position.
|
||||
*
|
||||
* @return the block x value
|
||||
*/
|
||||
int blockX();
|
||||
|
||||
/**
|
||||
* Gets the block y value for this position
|
||||
* Gets the block y value for this position.
|
||||
*
|
||||
* @return the block y value
|
||||
*/
|
||||
int blockY();
|
||||
|
||||
/**
|
||||
* Gets the block z value for this position
|
||||
* Gets the block z value for this position.
|
||||
*
|
||||
* @return the block z value
|
||||
*/
|
||||
int blockZ();
|
||||
|
||||
/**
|
||||
* Gets the x value for this position
|
||||
* Gets the x value for this position.
|
||||
*
|
||||
* @return the x value
|
||||
*/
|
||||
double x();
|
||||
|
||||
/**
|
||||
* Gets the y value for this position
|
||||
* Gets the y value for this position.
|
||||
*
|
||||
* @return the y value
|
||||
*/
|
||||
double y();
|
||||
|
||||
/**
|
||||
* Gets the z value for this position
|
||||
* Gets the z value for this position.
|
||||
*
|
||||
* @return the z value
|
||||
*/
|
||||
double z();
|
||||
|
||||
/**
|
||||
* Checks of this position represents a {@link BlockPosition}
|
||||
* Checks of this position represents a {@link BlockPosition}.
|
||||
*
|
||||
* @return true if block
|
||||
*/
|
||||
boolean isBlock();
|
||||
|
||||
/**
|
||||
* Checks if this position represents a {@link FinePosition}
|
||||
* Checks if this position represents a {@link FinePosition}.
|
||||
*
|
||||
* @return true if fine
|
||||
*/
|
||||
|
|
@ -77,6 +81,8 @@ public interface Position {
|
|||
|
||||
/**
|
||||
* Checks if each component of this position is finite.
|
||||
*
|
||||
* @return true if each component of this position is finite
|
||||
*/
|
||||
default boolean isFinite() {
|
||||
return Double.isFinite(this.x()) && Double.isFinite(this.y()) && Double.isFinite(this.z());
|
||||
|
|
@ -103,7 +109,7 @@ public interface Position {
|
|||
FinePosition offset(double x, double y, double z);
|
||||
|
||||
/**
|
||||
* Returns a new position at the center of the block position this represents
|
||||
* Returns a new position at the center of the block position this represents.
|
||||
*
|
||||
* @return a new center position
|
||||
*/
|
||||
|
|
@ -114,7 +120,7 @@ public interface Position {
|
|||
|
||||
/**
|
||||
* Returns the block position of this position
|
||||
* or itself if it already is a block position
|
||||
* or itself if it already is a block position.
|
||||
*
|
||||
* @return the block position
|
||||
*/
|
||||
|
|
@ -122,7 +128,7 @@ public interface Position {
|
|||
BlockPosition toBlock();
|
||||
|
||||
/**
|
||||
* Converts this position to a vector
|
||||
* Converts this position to a vector.
|
||||
*
|
||||
* @return a new vector
|
||||
*/
|
||||
|
|
@ -132,7 +138,7 @@ public interface Position {
|
|||
}
|
||||
|
||||
/**
|
||||
* Creates a new location object at this position with the specified world
|
||||
* Creates a new location object at this position with the specified world.
|
||||
*
|
||||
* @param world the world for the location object
|
||||
* @return a new location
|
||||
|
|
@ -143,7 +149,7 @@ public interface Position {
|
|||
}
|
||||
|
||||
/**
|
||||
* Creates a position at the coordinates
|
||||
* Creates a position at the coordinates.
|
||||
*
|
||||
* @param x x coord
|
||||
* @param y y coord
|
||||
|
|
@ -167,7 +173,7 @@ public interface Position {
|
|||
}
|
||||
|
||||
/**
|
||||
* Creates a position at the coordinates
|
||||
* Creates a position at the coordinates.
|
||||
*
|
||||
* @param x x coord
|
||||
* @param y y coord
|
||||
|
|
|
|||
|
|
@ -1,11 +1,8 @@
|
|||
package io.papermc.paper.math;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
/**
|
||||
* Represents a rotation with specified pitch and yaw values.
|
||||
*/
|
||||
@NullMarked
|
||||
public interface Rotation {
|
||||
/**
|
||||
* Creates a new rotation with the specified yaw and pitch values.
|
||||
|
|
|
|||
|
|
@ -1,21 +1,18 @@
|
|||
package io.papermc.paper.math;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
/**
|
||||
* Rotations is an immutable object that stores rotations
|
||||
* in degrees on each axis (X, Y, Z).
|
||||
*/
|
||||
@NullMarked
|
||||
public interface Rotations {
|
||||
|
||||
/**
|
||||
* Rotations instance with every axis set to 0
|
||||
* Rotations instance with every axis set to 0.
|
||||
*/
|
||||
Rotations ZERO = ofDegrees(0, 0, 0);
|
||||
|
||||
/**
|
||||
* Creates a new Rotations instance holding the provided rotations
|
||||
* Creates a new Rotations instance holding the provided rotations.
|
||||
*
|
||||
* @param x the angle for the X axis in degrees
|
||||
* @param y the angle for the Y axis in degrees
|
||||
|
|
@ -27,21 +24,21 @@ public interface Rotations {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the angle on the X axis in degrees
|
||||
* Returns the angle on the X axis in degrees.
|
||||
*
|
||||
* @return the angle in degrees
|
||||
*/
|
||||
double x();
|
||||
|
||||
/**
|
||||
* Returns the angle on the Y axis in degrees
|
||||
* Returns the angle on the Y axis in degrees.
|
||||
*
|
||||
* @return the angle in degrees
|
||||
*/
|
||||
double y();
|
||||
|
||||
/**
|
||||
* Returns the angle on the Z axis in degrees
|
||||
* Returns the angle on the Z axis in degrees.
|
||||
*
|
||||
* @return the angle in degrees
|
||||
*/
|
||||
|
|
@ -49,7 +46,7 @@ public interface Rotations {
|
|||
|
||||
/**
|
||||
* Returns a new Rotations instance which is the result
|
||||
* of changing the X axis to the passed angle
|
||||
* of changing the X axis to the passed angle.
|
||||
*
|
||||
* @param x the angle in degrees
|
||||
* @return the resultant Rotations
|
||||
|
|
@ -58,7 +55,7 @@ public interface Rotations {
|
|||
|
||||
/**
|
||||
* Returns a new Rotations instance which is the result
|
||||
* of changing the Y axis to the passed angle
|
||||
* of changing the Y axis to the passed angle.
|
||||
*
|
||||
* @param y the angle in degrees
|
||||
* @return the resultant Rotations
|
||||
|
|
@ -67,7 +64,7 @@ public interface Rotations {
|
|||
|
||||
/**
|
||||
* Returns a new Rotations instance which is the result
|
||||
* of changing the Z axis to the passed angle
|
||||
* of changing the Z axis to the passed angle.
|
||||
*
|
||||
* @param z the angle in degrees
|
||||
* @return the resultant Rotations
|
||||
|
|
@ -76,7 +73,7 @@ public interface Rotations {
|
|||
|
||||
/**
|
||||
* Returns a new Rotations instance which is the result of adding
|
||||
* the x, y, z components to this Rotations
|
||||
* the x, y, z components to this Rotations.
|
||||
*
|
||||
* @param x the angle to add to the X axis in degrees
|
||||
* @param y the angle to add to the Y axis in degrees
|
||||
|
|
@ -87,7 +84,7 @@ public interface Rotations {
|
|||
|
||||
/**
|
||||
* Returns a new Rotations instance which is the result of subtracting
|
||||
* the x, y, z components from this Rotations
|
||||
* the x, y, z components from this Rotations.
|
||||
*
|
||||
* @param x the angle to subtract from the X axis in degrees
|
||||
* @param y the angle to subtract from the Y axis in degrees
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
package io.papermc.paper.math;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
record RotationsImpl(double x, double y, double z) implements Rotations {
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
/**
|
||||
* Package containing maths-related API, including position API.
|
||||
*/
|
||||
@NullMarked
|
||||
package io.papermc.paper.math;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
/**
|
||||
* Root package for the Paper API.
|
||||
*/
|
||||
@NullMarked
|
||||
package io.papermc.paper;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
|
@ -4,56 +4,57 @@ import java.time.Instant;
|
|||
import java.util.Optional;
|
||||
import java.util.OptionalInt;
|
||||
import net.kyori.adventure.key.Key;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public class TestServerBuildInfo implements ServerBuildInfo {
|
||||
@Override
|
||||
public @NotNull Key brandId() {
|
||||
public Key brandId() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBrandCompatible(final @NotNull Key brandId) {
|
||||
public boolean isBrandCompatible(final Key brandId) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String brandName() {
|
||||
public String brandName() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String minecraftVersionId() {
|
||||
public String minecraftVersionId() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String minecraftVersionName() {
|
||||
public String minecraftVersionName() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull OptionalInt buildNumber() {
|
||||
public OptionalInt buildNumber() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Instant buildTime() {
|
||||
public Instant buildTime() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Optional<String> gitBranch() {
|
||||
public Optional<String> gitBranch() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Optional<String> gitCommit() {
|
||||
public Optional<String> gitCommit() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String asString(final @NotNull StringRepresentation representation) {
|
||||
public String asString(final StringRepresentation representation) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
9
paper-checkstyle/.checkstyle/checkstyle.xml
Normal file
9
paper-checkstyle/.checkstyle/checkstyle.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<!DOCTYPE module PUBLIC
|
||||
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
|
||||
"https://checkstyle.org/dtds/configuration_1_3.dtd">
|
||||
<module name="Checker">
|
||||
<module name="SuppressionSingleFilter">
|
||||
<property name="checks" value=".*"/>
|
||||
<property name="files" value="src[\\/]testData[\\/]java[^\\/]*[\\/].*"/>
|
||||
</module>
|
||||
</module>
|
||||
26
paper-checkstyle/build.gradle.kts
Normal file
26
paper-checkstyle/build.gradle.kts
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
plugins {
|
||||
java
|
||||
id("io.papermc.paperweight.paper-checkstyle")
|
||||
}
|
||||
|
||||
val testData = sourceSets.create("testData")
|
||||
|
||||
dependencies {
|
||||
implementation("com.puppycrawl.tools:checkstyle:13.8.0")
|
||||
implementation("org.jspecify:jspecify:1.0.0")
|
||||
|
||||
testCompileOnly("org.jetbrains:annotations:26.0.2")
|
||||
testImplementation(testData.output)
|
||||
|
||||
testImplementation("org.junit.jupiter:junit-jupiter:5.13.3")
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||
testData.implementationConfigurationName("org.jspecify:jspecify:1.0.0")
|
||||
|
||||
checkstyle(project(":paper-checkstyle"))
|
||||
}
|
||||
|
||||
tasks {
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
}
|
||||
185
paper-checkstyle/src/main/java/io/papermc/checkstyle/Util.java
Normal file
185
paper-checkstyle/src/main/java/io/papermc/checkstyle/Util.java
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
package io.papermc.checkstyle;
|
||||
|
||||
import com.puppycrawl.tools.checkstyle.JavaParser;
|
||||
import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
|
||||
import com.puppycrawl.tools.checkstyle.api.DetailAST;
|
||||
import com.puppycrawl.tools.checkstyle.api.DetailNode;
|
||||
import com.puppycrawl.tools.checkstyle.api.TokenTypes;
|
||||
import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* Utility class containing utility methods for custom checkstyle checks.
|
||||
*/
|
||||
public final class Util {
|
||||
|
||||
public static final Set<Integer> PRIMITIVE_TYPES = Set.of(
|
||||
TokenTypes.LITERAL_VOID,
|
||||
TokenTypes.LITERAL_BOOLEAN,
|
||||
TokenTypes.LITERAL_BYTE,
|
||||
TokenTypes.LITERAL_CHAR,
|
||||
TokenTypes.LITERAL_SHORT,
|
||||
TokenTypes.LITERAL_INT,
|
||||
TokenTypes.LITERAL_LONG,
|
||||
TokenTypes.LITERAL_FLOAT,
|
||||
TokenTypes.LITERAL_DOUBLE
|
||||
);
|
||||
|
||||
private Util() {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the previous sibling of the given node with the given type.
|
||||
*
|
||||
* @param node the node
|
||||
* @param type the type
|
||||
* @return the previous sibling with the given type, or {@code null} if not found
|
||||
*/
|
||||
public static @Nullable DetailNode getPreviousSibling(final DetailNode node, final int type) {
|
||||
DetailNode sibling = node.getPreviousSibling();
|
||||
while (sibling != null && sibling.getType() != type) {
|
||||
sibling = sibling.getPreviousSibling();
|
||||
}
|
||||
return sibling;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the next sibling of the given node with the given type.
|
||||
*
|
||||
* @param node the node
|
||||
* @param type the type
|
||||
* @return the next sibling with the given type, or {@code null} if not found
|
||||
*/
|
||||
public static @Nullable DetailAST getNextSibling(final DetailAST node, final int type) {
|
||||
DetailAST sibling = node.getNextSibling();
|
||||
while (sibling != null && sibling.getType() != type) {
|
||||
sibling = sibling.getNextSibling();
|
||||
}
|
||||
return sibling;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the last child of the given node with the given type.
|
||||
*
|
||||
* @param node the node
|
||||
* @param type the type
|
||||
* @return the last child with the given type or {@code null} if not found
|
||||
*/
|
||||
public static @Nullable DetailAST getLastChild(final DetailAST node, final int type) {
|
||||
DetailAST child = node.getLastChild();
|
||||
while (child != null && child.getType() != type) {
|
||||
child = child.getPreviousSibling();
|
||||
}
|
||||
return child;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the enclosing type declaration of the given node.
|
||||
*
|
||||
* @param node the node
|
||||
* @return the enclosing type declaration, or {@code null} if not found
|
||||
*/
|
||||
public static @Nullable DetailAST getEnclosingTypeDeclaration(final DetailAST node) {
|
||||
DetailAST parent = node.getParent();
|
||||
while (parent != null && !TokenUtil.isTypeDeclaration(parent.getType())) {
|
||||
parent = parent.getParent();
|
||||
}
|
||||
return parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an iterator over the children of the given node with the given type.
|
||||
*
|
||||
* @param ast the node
|
||||
* @param type the type
|
||||
* @return the iterator
|
||||
*/
|
||||
public static Iterable<DetailAST> childrenIterator(final DetailAST ast, final int type) {
|
||||
return () -> new Iterator<>() {
|
||||
private @Nullable DetailAST current = TokenUtil.findFirstTokenByPredicate(ast, child -> child.getType() == type).orElse(null);
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return this.current != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DetailAST next() {
|
||||
if (this.current == null) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
final DetailAST result = this.current;
|
||||
this.current = getNextSibling(this.current, type);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static @Nullable DetailAST findPackageInfoFor(final Path filePath) {
|
||||
final Path packageInfo = filePath.getParent().resolve("package-info.java");
|
||||
if (Files.notExists(packageInfo)) {
|
||||
return null;
|
||||
}
|
||||
final DetailAST packageInfoAst;
|
||||
try {
|
||||
packageInfoAst = JavaParser.parseFile(packageInfo.toFile(), JavaParser.Options.WITHOUT_COMMENTS);
|
||||
} catch (final IOException | CheckstyleException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return packageInfoAst;
|
||||
}
|
||||
|
||||
public static boolean isPackageInfoAnnotated(final Path filePath, final Predicate<? super DetailAST> annotationPredicate) {
|
||||
final DetailAST packageInfoAst = Util.findPackageInfoFor(filePath);
|
||||
if (packageInfoAst == null) {
|
||||
return false;
|
||||
}
|
||||
final DetailAST firstToken = packageInfoAst.findFirstToken(TokenTypes.PACKAGE_DEF);
|
||||
if (firstToken == null) {
|
||||
return false;
|
||||
}
|
||||
final DetailAST annotations = firstToken.findFirstToken(TokenTypes.ANNOTATIONS);
|
||||
if (annotations == null) {
|
||||
return false;
|
||||
}
|
||||
for (final DetailAST annotation : Util.childrenIterator(annotations, TokenTypes.ANNOTATION)) {
|
||||
if (annotationPredicate.test(annotation)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Pair<String, DetailAST> extractFullIdent(final DetailAST lastDot) {
|
||||
final List<String> parts = new ArrayList<>();
|
||||
DetailAST dot = lastDot;
|
||||
while (dot.getChildCount(TokenTypes.DOT) > 0) {
|
||||
parts.addFirst(requireNonNull(dot.findFirstToken(TokenTypes.IDENT)).getText());
|
||||
dot = requireNonNull(dot.findFirstToken(TokenTypes.DOT));
|
||||
}
|
||||
if (dot.getChildCount(TokenTypes.IDENT) > 2) {
|
||||
throw new IllegalArgumentException("Invalid AST structure, expected <= 2 IDENTs " + dot.getChildCount(TokenTypes.IDENT) + " " + dot.getLineNo() + " " + dot.getColumnNo());
|
||||
}
|
||||
final DetailAST ident = requireNonNull(dot.findFirstToken(TokenTypes.IDENT));
|
||||
final DetailAST secondIdent = getNextSibling(ident, TokenTypes.IDENT);
|
||||
if (secondIdent != null) {
|
||||
parts.addFirst(secondIdent.getText());
|
||||
}
|
||||
parts.addFirst(ident.getText());
|
||||
|
||||
return Pair.of(String.join(".", parts), ident);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
package io.papermc.checkstyle.checks;
|
||||
|
||||
import com.puppycrawl.tools.checkstyle.api.DetailNode;
|
||||
import com.puppycrawl.tools.checkstyle.api.JavadocCommentsTokenTypes;
|
||||
import com.puppycrawl.tools.checkstyle.checks.javadoc.AbstractJavadocCheck;
|
||||
import com.puppycrawl.tools.checkstyle.utils.JavadocUtil;
|
||||
import io.papermc.checkstyle.Util;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Checks that parameter descriptions in Javadoc are aligned.
|
||||
*/
|
||||
public final class JavadocAlignParameterDescriptionCheck extends AbstractJavadocCheck {
|
||||
|
||||
static final Pattern SPACE_PREFIX = Pattern.compile("^\\s*");
|
||||
|
||||
@Override
|
||||
public int[] getDefaultJavadocTokens() {
|
||||
return new int[]{JavadocCommentsTokenTypes.JAVADOC_CONTENT};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitJavadocToken(final DetailNode detailNode) {
|
||||
record ParamDesc(DetailNode node, int startCol) {
|
||||
}
|
||||
|
||||
final List<ParamDesc> params = new ArrayList<>();
|
||||
int maxColumn = -1;
|
||||
DetailNode child = detailNode.getFirstChild();
|
||||
for (final DetailNode javadocBlock : JavadocUtil.getAllNodesOfType(detailNode, JavadocCommentsTokenTypes.JAVADOC_BLOCK_TAG)) {
|
||||
final DetailNode paramBlock = JavadocUtil.findFirstToken(javadocBlock, JavadocCommentsTokenTypes.PARAM_BLOCK_TAG);
|
||||
if (paramBlock == null) {
|
||||
continue;
|
||||
}
|
||||
final DetailNode paramDescription = JavadocUtil.findFirstToken(paramBlock, JavadocCommentsTokenTypes.DESCRIPTION);
|
||||
if (paramDescription == null) {
|
||||
continue;
|
||||
}
|
||||
// iterate over all text nodes (multiline)
|
||||
for (final DetailNode textNode : JavadocUtil.getAllNodesOfType(paramDescription, JavadocCommentsTokenTypes.TEXT)) {
|
||||
final Matcher matcher = SPACE_PREFIX.matcher(textNode.getText());
|
||||
int paramDescColNum = textNode.getColumnNumber();
|
||||
if (matcher.find()) {
|
||||
paramDescColNum += matcher.group().length();
|
||||
}
|
||||
maxColumn = Math.max(maxColumn, paramDescColNum);
|
||||
params.add(new ParamDesc(textNode, paramDescColNum));
|
||||
}
|
||||
child = child.getNextSibling();
|
||||
}
|
||||
|
||||
for (final ParamDesc param : params) {
|
||||
if (param.startCol() != maxColumn) {
|
||||
final DetailNode paramNameNode = Util.getPreviousSibling(param.node().getParent(), JavadocCommentsTokenTypes.PARAMETER_NAME);
|
||||
if (paramNameNode == null) {
|
||||
continue;
|
||||
}
|
||||
this.log(
|
||||
param.node().getLineNumber(),
|
||||
param.startCol(),
|
||||
"Param description for %s should start at column %d".formatted(paramNameNode.getText(), maxColumn + 1)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
package io.papermc.checkstyle.checks;
|
||||
|
||||
import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
|
||||
import com.puppycrawl.tools.checkstyle.api.DetailAST;
|
||||
import com.puppycrawl.tools.checkstyle.api.TokenTypes;
|
||||
import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
|
||||
import io.papermc.checkstyle.Util;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Set;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Checks that nullability annotations are present where required.
|
||||
*/
|
||||
public final class NullabilityAnnotationsCheck extends AbstractCheck {
|
||||
|
||||
private static final Set<String> NULLABILITY_ANNOTATIONS = Set.of("Nullable", "NonNull");
|
||||
|
||||
@Override
|
||||
public int[] getDefaultTokens() {
|
||||
return this.getRequiredTokens();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getAcceptableTokens() {
|
||||
return this.getRequiredTokens();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getRequiredTokens() {
|
||||
return new int[]{
|
||||
TokenTypes.METHOD_DEF,
|
||||
TokenTypes.PARAMETER_DEF,
|
||||
TokenTypes.ANNOTATION_FIELD_DEF,
|
||||
TokenTypes.RECORD_COMPONENT_DEF, // annotations are in ANNOTATIONS token block
|
||||
};
|
||||
}
|
||||
|
||||
private static boolean hasNoNullabilityAnnotationChildren(final @Nullable DetailAST ast) {
|
||||
if (ast == null) {
|
||||
return true;
|
||||
}
|
||||
for (final DetailAST annotation : Util.childrenIterator(ast, TokenTypes.ANNOTATION)) {
|
||||
if (annotation.getChildCount(TokenTypes.IDENT) != 1) {
|
||||
// skip `.` annotations like ApiStatus.Internal as these aren't nullability annotations
|
||||
continue;
|
||||
}
|
||||
final String ident = annotation.findFirstToken(TokenTypes.IDENT).getText();
|
||||
if (NULLABILITY_ANNOTATIONS.contains(ident)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void visitMethodDefOrParamDef(final DetailAST holderDef, final int baseAnnotationHolderType) {
|
||||
final DetailAST type = holderDef.findFirstToken(TokenTypes.TYPE);
|
||||
final DetailAST arrayTypeStart = type.findFirstToken(TokenTypes.ARRAY_DECLARATOR);
|
||||
if (arrayTypeStart != null) {
|
||||
final DetailAST arrayAnnotations = type.findFirstToken(TokenTypes.ANNOTATIONS);
|
||||
if (hasNoNullabilityAnnotationChildren(arrayAnnotations)) {
|
||||
this.log(arrayTypeStart.getLineNo(), arrayTypeStart.getColumnNo() - 1, "Array is missing nullability annotation");
|
||||
}
|
||||
}
|
||||
if (TokenUtil.findFirstTokenByPredicate(type, t -> Util.PRIMITIVE_TYPES.contains(t.getType())).isPresent()) {
|
||||
// skip primitive types
|
||||
return;
|
||||
}
|
||||
final DetailAST dot = type.findFirstToken(TokenTypes.DOT);
|
||||
final DetailAST annotationHolder;
|
||||
final DetailAST identLoc;
|
||||
if (dot != null) {
|
||||
annotationHolder = dot.findFirstToken(TokenTypes.ANNOTATIONS);
|
||||
identLoc = Util.getLastChild(dot, TokenTypes.IDENT);
|
||||
} else {
|
||||
annotationHolder = holderDef.findFirstToken(baseAnnotationHolderType);
|
||||
identLoc = type;
|
||||
}
|
||||
if (hasNoNullabilityAnnotationChildren(annotationHolder)) {
|
||||
this.log(identLoc.getLineNo(), identLoc.getColumnNo(), "Missing nullability annotation for '" + holderDef.findFirstToken(TokenTypes.IDENT).getText() + "'");
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isNullMarkedAnnotation(final DetailAST annotation) {
|
||||
if (annotation.getChildCount(TokenTypes.IDENT) != 1) {
|
||||
return false;
|
||||
}
|
||||
final String ident = annotation.findFirstToken(TokenTypes.IDENT).getText();
|
||||
return "NullMarked".equals(ident);
|
||||
}
|
||||
|
||||
public static @Nullable DetailAST getNullMarkedAnnotation(final DetailAST typeDeclaration) {
|
||||
final DetailAST modifiers = typeDeclaration.findFirstToken(TokenTypes.MODIFIERS);
|
||||
if (modifiers == null) {
|
||||
return null;
|
||||
}
|
||||
for (final DetailAST annotation : Util.childrenIterator(modifiers, TokenTypes.ANNOTATION)) {
|
||||
if (isNullMarkedAnnotation(annotation)) {
|
||||
return annotation;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitToken(final DetailAST ast) {
|
||||
if (Util.isPackageInfoAnnotated(Path.of(this.getFilePath()), NullabilityAnnotationsCheck::isNullMarkedAnnotation)) {
|
||||
return;
|
||||
}
|
||||
for (DetailAST parentDef = Util.getEnclosingTypeDeclaration(ast); parentDef != null; parentDef = Util.getEnclosingTypeDeclaration(parentDef)) {
|
||||
if (getNullMarkedAnnotation(parentDef) != null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
switch (ast.getType()) {
|
||||
case TokenTypes.METHOD_DEF, TokenTypes.PARAMETER_DEF, TokenTypes.ANNOTATION_FIELD_DEF -> this.visitMethodDefOrParamDef(ast, TokenTypes.MODIFIERS);
|
||||
case TokenTypes.RECORD_COMPONENT_DEF -> this.visitMethodDefOrParamDef(ast, TokenTypes.ANNOTATIONS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package io.papermc.checkstyle.checks;
|
||||
|
||||
import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
|
||||
import com.puppycrawl.tools.checkstyle.api.DetailAST;
|
||||
import com.puppycrawl.tools.checkstyle.api.TokenTypes;
|
||||
import io.papermc.checkstyle.Util;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class RedundantNullabilityCheck extends AbstractCheck {
|
||||
|
||||
@Override
|
||||
public int[] getDefaultTokens() {
|
||||
return this.getRequiredTokens();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getAcceptableTokens() {
|
||||
return this.getRequiredTokens();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getRequiredTokens() {
|
||||
return new int[]{
|
||||
TokenTypes.CLASS_DEF,
|
||||
TokenTypes.INTERFACE_DEF,
|
||||
TokenTypes.ANNOTATION_DEF,
|
||||
TokenTypes.RECORD_DEF,
|
||||
TokenTypes.ENUM_DEF,
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitToken(final DetailAST ast) {
|
||||
final boolean pkgIsNullMarked = Util.isPackageInfoAnnotated(Path.of(this.getFilePath()), NullabilityAnnotationsCheck::isNullMarkedAnnotation);
|
||||
final DetailAST nullMarkedAnnotation = NullabilityAnnotationsCheck.getNullMarkedAnnotation(ast);
|
||||
if (pkgIsNullMarked && nullMarkedAnnotation != null) {
|
||||
this.log(nullMarkedAnnotation.getLineNo(), ast.getColumnNo() - 1, "Redundant NullMarked annotation");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
package io.papermc.checkstyle.checks;
|
||||
|
||||
import com.puppycrawl.tools.checkstyle.api.DetailAST;
|
||||
import com.puppycrawl.tools.checkstyle.api.TokenTypes;
|
||||
import io.papermc.checkstyle.Util;
|
||||
import io.papermc.checkstyle.checks.util.ImportAwareAbstractCheck;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Requires that the codebase be set up to follow standard naming conventions.
|
||||
* <ul>
|
||||
* <li>packages are always all lowercase</li>
|
||||
* <li>class names start with a capital letter</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class UnnecessaryFullyQualifiedImportCheck extends ImportAwareAbstractCheck {
|
||||
|
||||
@Override
|
||||
public int[] getDefaultTokens() {
|
||||
return this.getRequiredTokens();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getAcceptableTokens() {
|
||||
return this.getRequiredTokens();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getRequiredTokens() {
|
||||
return new int[]{
|
||||
TokenTypes.TYPE,
|
||||
TokenTypes.TYPE_ARGUMENT,
|
||||
TokenTypes.TYPE_PARAMETER,
|
||||
TokenTypes.TYPE_UPPER_BOUNDS,
|
||||
TokenTypes.TYPE_LOWER_BOUNDS,
|
||||
TokenTypes.EXTENDS_CLAUSE,
|
||||
TokenTypes.PERMITS_CLAUSE,
|
||||
TokenTypes.IMPLEMENTS_CLAUSE,
|
||||
TokenTypes.LITERAL_NEW,
|
||||
TokenTypes.ANNOTATION,
|
||||
TokenTypes.METHOD_CALL,
|
||||
TokenTypes.LITERAL_CLASS,
|
||||
};
|
||||
}
|
||||
|
||||
private static @Nullable DetailAST fromMethodCall(final DetailAST methodCall) {
|
||||
final DetailAST dot = methodCall.findFirstToken(TokenTypes.DOT);
|
||||
if (dot == null) {
|
||||
return null;
|
||||
}
|
||||
final DetailAST nextDot = dot.findFirstToken(TokenTypes.DOT);
|
||||
if (nextDot == null) {
|
||||
return null;
|
||||
}
|
||||
final DetailAST ident = nextDot.findFirstToken(TokenTypes.IDENT);
|
||||
if (ident == null || Character.isLowerCase(ident.getText().charAt(0))) {
|
||||
// method call not on a type
|
||||
return null;
|
||||
}
|
||||
return dot;
|
||||
}
|
||||
|
||||
private static @Nullable DetailAST fromLiteralClass(final DetailAST literalClass) {
|
||||
final DetailAST dot = literalClass.getParent();
|
||||
if (dot.getType() != TokenTypes.DOT) {
|
||||
return null;
|
||||
}
|
||||
return dot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitToken(final DetailAST ast) {
|
||||
final DetailAST actualAst = switch (ast.getType()) {
|
||||
case TokenTypes.METHOD_CALL -> fromMethodCall(ast);
|
||||
case TokenTypes.LITERAL_CLASS -> fromLiteralClass(ast);
|
||||
default -> ast;
|
||||
};
|
||||
if (actualAst == null) {
|
||||
return;
|
||||
}
|
||||
final DetailAST dot = actualAst.findFirstToken(TokenTypes.DOT);
|
||||
if (dot == null) {
|
||||
return;
|
||||
}
|
||||
final Pair<String, DetailAST> fullIdent = Util.extractFullIdent(dot);
|
||||
final DetailAST startNode = fullIdent.getRight();
|
||||
final String fullIdentName = fullIdent.getLeft();
|
||||
if (Character.isUpperCase(fullIdentName.charAt(0))) {
|
||||
// not a package
|
||||
return;
|
||||
}
|
||||
String previousTypeName;
|
||||
String typeName = fullIdentName;
|
||||
int lastDotIdx = typeName.lastIndexOf('.');
|
||||
do {
|
||||
// Check if we have an import for this exact type
|
||||
if (this.hasImportFor(typeName)) {
|
||||
this.log(startNode.getLineNo(), startNode.getColumnNo(), "Fully qualified import used: " + fullIdentName);
|
||||
return;
|
||||
}
|
||||
previousTypeName = typeName;
|
||||
typeName = typeName.substring(0, lastDotIdx);
|
||||
} while ((lastDotIdx = typeName.lastIndexOf('.')) != -1 && Character.isUpperCase(typeName.substring(0, lastDotIdx + 2).charAt(lastDotIdx + 1)));
|
||||
|
||||
final String outerSimpleName = previousTypeName.substring(previousTypeName.lastIndexOf('.') + 1);
|
||||
final String fullImportBySimple = this.getFullImportBySimple(outerSimpleName);
|
||||
if (fullImportBySimple == null) {
|
||||
this.log(startNode.getLineNo(), startNode.getColumnNo(), "Unnecessary fully qualified import (no conflict): " + fullIdentName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
/**
|
||||
* Custom checkstyle checks for PaperMC projects.
|
||||
*/
|
||||
@NullMarked
|
||||
@SuppressWarnings("unused")
|
||||
package io.papermc.checkstyle.checks;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
package io.papermc.checkstyle.checks.util;
|
||||
|
||||
import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
|
||||
import com.puppycrawl.tools.checkstyle.api.DetailAST;
|
||||
import com.puppycrawl.tools.checkstyle.api.FullIdent;
|
||||
import com.puppycrawl.tools.checkstyle.api.TokenTypes;
|
||||
import io.papermc.checkstyle.Util;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
public abstract class ImportAwareAbstractCheck extends AbstractCheck {
|
||||
|
||||
private final ThreadLocal<Set<String>> imports = ThreadLocal.withInitial(LinkedHashSet::new);
|
||||
private final ThreadLocal<Map<String, String>> simpleImports = ThreadLocal.withInitial(LinkedHashMap::new);
|
||||
|
||||
@Override
|
||||
public void beginTree(final DetailAST rootAST) {
|
||||
final Set<String> imports = new LinkedHashSet<>();
|
||||
final Map<String, String> simpleImports = new LinkedHashMap<>();
|
||||
for (final DetailAST importAst : Util.childrenIterator(rootAST, TokenTypes.IMPORT)) {
|
||||
extractName(importAst, imports, simpleImports);
|
||||
}
|
||||
this.imports.set(imports);
|
||||
this.simpleImports.set(simpleImports);
|
||||
}
|
||||
|
||||
private static void extractName(final DetailAST parent, final Set<String> fullImports, final Map<String, String> simpleImports) {
|
||||
final DetailAST lastDot = Objects.requireNonNull(parent.findFirstToken(TokenTypes.DOT));
|
||||
if (lastDot.getChildCount(TokenTypes.STAR) > 0) {
|
||||
// skip wildcard imports
|
||||
return;
|
||||
}
|
||||
final DetailAST ident = Objects.requireNonNull(lastDot.findFirstToken(TokenTypes.IDENT));
|
||||
final String fullImport = FullIdent.createFullIdent(lastDot).getText();
|
||||
fullImports.add(fullImport);
|
||||
simpleImports.put(ident.getText(), fullImport);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finishTree(final DetailAST rootAST) {
|
||||
this.imports.remove();
|
||||
}
|
||||
|
||||
protected boolean hasImportFor(final String type) {
|
||||
return this.imports.get().contains(type);
|
||||
}
|
||||
|
||||
protected @Nullable String getFullImportBySimple(final String simpleName) {
|
||||
return this.simpleImports.get().get(simpleName);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
/**
|
||||
* Utility classes for checks.
|
||||
*/
|
||||
@NullMarked
|
||||
package io.papermc.checkstyle.checks.util;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
package io.papermc.checkstyle.filters;
|
||||
|
||||
import com.puppycrawl.tools.checkstyle.AbstractAutomaticBean;
|
||||
import com.puppycrawl.tools.checkstyle.api.AuditEvent;
|
||||
import com.puppycrawl.tools.checkstyle.api.Filter;
|
||||
import io.papermc.checkstyle.filters.util.BetterSuppressFilterElement;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
public class BetterSuppressionSingleFilter extends AbstractAutomaticBean implements Filter {
|
||||
|
||||
private @Nullable BetterSuppressFilterElement filter;
|
||||
private @Nullable Pattern checks;
|
||||
private @Nullable Pattern messageKey;
|
||||
private @Nullable List<Pattern> arguments;
|
||||
|
||||
public void setChecks(final Pattern checks) {
|
||||
this.checks = checks;
|
||||
}
|
||||
|
||||
public void setMessageKey(final Pattern messageKey) {
|
||||
this.messageKey = messageKey;
|
||||
}
|
||||
|
||||
public void setArguments(final String... arguments) {
|
||||
this.arguments = Arrays.stream(arguments).map(Pattern::compile).toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finishLocalSetup() {
|
||||
this.filter = new BetterSuppressFilterElement(
|
||||
null,
|
||||
this.checks,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
this.messageKey,
|
||||
this.arguments
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accept(final AuditEvent event) {
|
||||
return this.filter.accept(event);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
/**
|
||||
* Custom filters for Checkstyle.
|
||||
*/
|
||||
@NullMarked
|
||||
package io.papermc.checkstyle.filters;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
package io.papermc.checkstyle.filters.util;
|
||||
|
||||
import com.puppycrawl.tools.checkstyle.api.AuditEvent;
|
||||
import com.puppycrawl.tools.checkstyle.api.Violation;
|
||||
import com.puppycrawl.tools.checkstyle.filters.SuppressFilterElement;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.IntStream;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
public class BetterSuppressFilterElement extends SuppressFilterElement {
|
||||
|
||||
private static final MethodHandle VIOLATION_ARGS;
|
||||
|
||||
static {
|
||||
try {
|
||||
VIOLATION_ARGS = MethodHandles.privateLookupIn(Violation.class, MethodHandles.lookup()).findGetter(Violation.class, "args", Object[].class);
|
||||
} catch (final IllegalAccessException | NoSuchFieldException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private final @Nullable Pattern messageKey;
|
||||
private final @Nullable List<Pattern> arguments;
|
||||
|
||||
public BetterSuppressFilterElement(
|
||||
final @Nullable Pattern files,
|
||||
final @Nullable Pattern checks,
|
||||
final @Nullable Pattern message,
|
||||
final @Nullable String moduleId,
|
||||
final @Nullable String lines,
|
||||
final @Nullable String columns,
|
||||
final @Nullable Pattern messageKey,
|
||||
final @Nullable List<Pattern> arguments
|
||||
) {
|
||||
super(files, checks, message, moduleId, lines, columns);
|
||||
this.messageKey = messageKey;
|
||||
this.arguments = arguments;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accept(final AuditEvent event) {
|
||||
return super.accept(event) || !this.isMessageKeyMatching(event) || !this.isMessageArgumentsMatching(event);
|
||||
}
|
||||
|
||||
private boolean isMessageKeyMatching(final AuditEvent event) {
|
||||
return this.messageKey == null || this.messageKey.matcher(event.getViolation().getKey()).find();
|
||||
}
|
||||
|
||||
private boolean isMessageArgumentsMatching(final AuditEvent event) {
|
||||
if (this.arguments == null || this.arguments.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
final Object[] args;
|
||||
try {
|
||||
args = ((Object[]) VIOLATION_ARGS.invoke(event.getViolation()));
|
||||
} catch (final Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return IntStream.range(0, Math.min(this.arguments.size(), args.length)).allMatch(i -> {
|
||||
final Object arg = args[i];
|
||||
final Pattern argPattern = this.arguments.get(i);
|
||||
return argPattern.matcher(arg.toString()).find();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
/**
|
||||
* Utility classes for filters.
|
||||
*/
|
||||
@NullMarked
|
||||
package io.papermc.checkstyle.filters.util;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
/**
|
||||
* This package contains custom checkstyle rules for PaperMC projects.
|
||||
*/
|
||||
@NullMarked
|
||||
package io.papermc.checkstyle;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!DOCTYPE checkstyle-packages PUBLIC
|
||||
"-//Checkstyle//DTD Package Names Configuration 1.0//EN"
|
||||
"https://checkstyle.org/dtds/packages_1_0.dtd">
|
||||
|
||||
<checkstyle-packages>
|
||||
<package name="io.papermc.checkstyle.checks"/>
|
||||
</checkstyle-packages>
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
package io.papermc.checkstyle;
|
||||
|
||||
import com.puppycrawl.tools.checkstyle.DefaultConfiguration;
|
||||
import com.puppycrawl.tools.checkstyle.PackageObjectFactory;
|
||||
import com.puppycrawl.tools.checkstyle.TreeWalker;
|
||||
import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
|
||||
import com.puppycrawl.tools.checkstyle.api.FileText;
|
||||
import com.puppycrawl.tools.checkstyle.api.Violation;
|
||||
import java.io.File;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import org.apache.commons.lang3.reflect.MethodUtils;
|
||||
import org.intellij.lang.annotations.Language;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.AfterTestExecutionCallback;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||
import org.junit.jupiter.api.extension.ParameterContext;
|
||||
import org.junit.jupiter.api.extension.ParameterResolutionException;
|
||||
import org.junit.jupiter.api.extension.ParameterResolver;
|
||||
import org.junit.platform.commons.support.AnnotationSupport;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
@ExtendWith({CheckTest.Parameters.class, CheckTest.RunCheckTest.class})
|
||||
@Test
|
||||
public @interface CheckTest {
|
||||
Object BUILDER_KEY = new Object();
|
||||
|
||||
@Language("jvm-class-name")
|
||||
String value();
|
||||
|
||||
final class Parameters implements ParameterResolver {
|
||||
|
||||
@Override
|
||||
public boolean supportsParameter(final ParameterContext parameterContext, final ExtensionContext extensionContext) throws ParameterResolutionException {
|
||||
return parameterContext.getParameter().getType() == CheckTestBuilder.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object resolveParameter(final ParameterContext parameterContext, final ExtensionContext extensionContext) throws ParameterResolutionException {
|
||||
final CheckTestBuilder builder = new CheckTestBuilder();
|
||||
extensionContext.getStore(ExtensionContext.Namespace.GLOBAL).put(CheckTest.BUILDER_KEY, builder);
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
||||
final class RunCheckTest implements AfterTestExecutionCallback {
|
||||
|
||||
@Override
|
||||
public void afterTestExecution(final ExtensionContext context) throws Exception {
|
||||
final CheckTest checkTest = AnnotationSupport.findAnnotation(context.getTestMethod(), CheckTest.class).orElseThrow();
|
||||
final CheckTestBuilder builder = context.getStore(ExtensionContext.Namespace.GLOBAL).get(CheckTest.BUILDER_KEY, CheckTestBuilder.class);
|
||||
assertNotNull(builder, "Method must have a CheckTestBuilder parameter");
|
||||
assertFalse(builder.checks.isEmpty(), "CheckTestBuilder must have at least one check");
|
||||
final TreeWalker walker = new TreeWalker();
|
||||
walker.setModuleFactory(new PackageObjectFactory("io.papermc.checkstyle.checks", Util.class.getClassLoader()));
|
||||
for (final AbstractCheck check : builder.checks) {
|
||||
check.init();
|
||||
check.configure(new DefaultConfiguration("Test"));
|
||||
MethodUtils.invokeMethod(walker, true, "registerCheck", check);
|
||||
}
|
||||
final Path filePath = Path.of("src", "testData", "java", checkTest.value().replace('.', File.separatorChar) + ".java");
|
||||
assertTrue(Files.exists(filePath), "File not found: " + filePath);
|
||||
assertFalse(Files.isDirectory(filePath), "File is a directory: " + filePath);
|
||||
final File file = filePath.toFile();
|
||||
final Set<Violation> violations = walker.process(file, new FileText(file, StandardCharsets.UTF_8.name()));
|
||||
final Set<String> violationSet = new LinkedHashSet<>(violations.stream().map(v -> "%d:%d: %s".formatted(v.getLineNo(), v.getColumnNo(), v.getViolation())).toList());
|
||||
final Set<String> expectedSet = new LinkedHashSet<>(builder.expectedViolations);
|
||||
final Set<String> extraViolations = new LinkedHashSet<>(violationSet);
|
||||
extraViolations.removeAll(expectedSet);
|
||||
final Set<String> missingViolations = new LinkedHashSet<>(expectedSet);
|
||||
missingViolations.removeAll(violationSet);
|
||||
if (!extraViolations.isEmpty() || !missingViolations.isEmpty()) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append("Violations do not match for ").append(filePath).append("\n");
|
||||
if (!extraViolations.isEmpty()) {
|
||||
sb.append("Violations not accounted for in test: \n");
|
||||
extraViolations.forEach(v -> sb.append(" ").append(v).append("\n"));
|
||||
}
|
||||
if (!missingViolations.isEmpty()) {
|
||||
sb.append("Violations in tests that don't exist: \n");
|
||||
missingViolations.forEach(v -> sb.append(" ").append(v).append("\n"));
|
||||
}
|
||||
throw new AssertionError(sb.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package io.papermc.checkstyle;
|
||||
|
||||
import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class CheckTestBuilder {
|
||||
|
||||
final List<AbstractCheck> checks = new ArrayList<>();
|
||||
final List<String> expectedViolations = new ArrayList<>();
|
||||
|
||||
public CheckTestBuilder addCheck(final AbstractCheck check) {
|
||||
this.checks.add(check);
|
||||
return this;
|
||||
}
|
||||
|
||||
public CheckTestBuilder addViolation(final String expectedViolation) {
|
||||
this.expectedViolations.add(expectedViolation);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package io.papermc.checkstyle.checks;
|
||||
|
||||
import io.papermc.checkstyle.CheckTest;
|
||||
import io.papermc.checkstyle.CheckTestBuilder;
|
||||
|
||||
class TestJavadocAlignParameterDescriptionCheck {
|
||||
|
||||
@CheckTest("testdata.JavadocParamAlignment")
|
||||
void test(final CheckTestBuilder builder) {
|
||||
builder
|
||||
.addCheck(new JavadocAlignParameterDescriptionCheck())
|
||||
.addViolation("10:17: Param description for a should start at column 34")
|
||||
.addViolation("30:23: Param description for a should start at column 34")
|
||||
.addViolation("32:30: Param description for superLongParamName should start at column 34");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package io.papermc.checkstyle.checks;
|
||||
|
||||
import io.papermc.checkstyle.CheckTest;
|
||||
import io.papermc.checkstyle.CheckTestBuilder;
|
||||
|
||||
class TestNullabilityAnnotationsCheck {
|
||||
|
||||
@CheckTest("testdata.NullabilityAnnotations")
|
||||
void test(final CheckTestBuilder builder) {
|
||||
builder
|
||||
.addViolation("13:12: Missing nullability annotation for method2")
|
||||
.addViolation("13:34: Missing nullability annotation for param")
|
||||
.addViolation("17:27: Array is missing nullability annotation")
|
||||
.addViolation("21:12: Missing nullability annotation for missingTypeAnnotation")
|
||||
.addViolation("25:14: Array is missing nullability annotation")
|
||||
.addViolation("25:34: Array is missing nullability annotation")
|
||||
.addViolation("43:57: Missing nullability annotation for param")
|
||||
.addViolation("49:73: Missing nullability annotation for param")
|
||||
.addCheck(new NullabilityAnnotationsCheck());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package io.papermc.checkstyle.checks;
|
||||
|
||||
import io.papermc.checkstyle.CheckTest;
|
||||
import io.papermc.checkstyle.CheckTestBuilder;
|
||||
|
||||
class TestUnnecessaryFullyQualifiedImportCheck {
|
||||
|
||||
@CheckTest("testdata.imports.UnnecessaryFullyQualifiedImport")
|
||||
void test(final CheckTestBuilder builder) {
|
||||
builder
|
||||
// variable def
|
||||
.addViolation("20:15: Fully qualified import used: testdata.imports.testclasses.subpkg.OtherClass")
|
||||
// new literal
|
||||
.addViolation("20:84: Fully qualified import used: testdata.imports.testclasses.subpkg.OtherClass")
|
||||
// variable def
|
||||
.addViolation("21:15: Fully qualified import used: testdata.imports.testclasses.subpkg.OtherClass.InnerClass")
|
||||
// new literal
|
||||
.addViolation("21:95: Fully qualified import used: testdata.imports.testclasses.subpkg.OtherClass.InnerClass")
|
||||
// type parm def (extends)
|
||||
.addViolation("29:23: Fully qualified import used: testdata.imports.testclasses.subpkg.OtherClass.InnerClass")
|
||||
// type argument, type param super
|
||||
.addViolation("29:115: Fully qualified import used: testdata.imports.testclasses.subpkg.OtherClass.InnerClass")
|
||||
.addViolation("37:23: Fully qualified import used: testdata.imports.testclasses.subpkg.OtherClass.InnerClass")
|
||||
.addViolation("37:71: Unnecessary fully qualified import (no conflict): org.jspecify.annotations.Nullable")
|
||||
.addViolation("37:143: Fully qualified import used: testdata.imports.testclasses.subpkg.OtherClass.InnerClass")
|
||||
.addViolation("46:41: Fully qualified import used: testdata.imports.testclasses.subpkg.OtherClass.InnerInterface")
|
||||
.addViolation("50:30: Unnecessary fully qualified import (no conflict): testdata.imports.testclasses.subpkg.OtherInterface")
|
||||
.addViolation("54:9: Unnecessary fully qualified import (no conflict): testdata.imports.testclasses.subpkg.OtherInterface")
|
||||
.addViolation("54:61: Unnecessary fully qualified import (no conflict): testdata.imports.testclasses.subpkg.OtherInterface")
|
||||
.addViolation("55:9: Unnecessary fully qualified import (no conflict): testdata.imports.testclasses.subpkg.OtherInterface")
|
||||
// field def
|
||||
.addViolation("66:5: Fully qualified import used: testdata.imports.testclasses.subpkg.OtherClass")
|
||||
// class literal ".class"
|
||||
.addViolation("70:28: Fully qualified import used: testdata.imports.testclasses.subpkg.OtherClass")
|
||||
.addCheck(new UnnecessaryFullyQualifiedImportCheck());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
@NullMarked
|
||||
package io.papermc.checkstyle.checks;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
@NullMarked
|
||||
package io.papermc.checkstyle;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
48
paper-checkstyle/src/testData/java/testdata/JavadocParamAlignment.java
vendored
Normal file
48
paper-checkstyle/src/testData/java/testdata/JavadocParamAlignment.java
vendored
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
// @formatter:off
|
||||
package testdata;
|
||||
|
||||
@SuppressWarnings("ALL")
|
||||
public class JavadocParamAlignment {
|
||||
|
||||
/**
|
||||
* A boring method.
|
||||
*
|
||||
* @param a some description
|
||||
* @param superLongParamName some description
|
||||
*/
|
||||
public void method(String a, String superLongParamName) {
|
||||
}
|
||||
|
||||
/**
|
||||
* A boring method.
|
||||
*
|
||||
* @param a some description
|
||||
* @param superLongParamName some description
|
||||
* @param another param
|
||||
*/
|
||||
public void correct_method(String a, String superLongParamName) {
|
||||
}
|
||||
|
||||
/**
|
||||
* A boring method.
|
||||
*
|
||||
* @param a some description
|
||||
* with extra text
|
||||
* @param superLongParamName some description
|
||||
* blah
|
||||
*/
|
||||
public void method_multiline(String a, String superLongParamName) {
|
||||
}
|
||||
|
||||
/**
|
||||
* A boring method.
|
||||
*
|
||||
* @param a some description
|
||||
* with extra text
|
||||
* @param superLongParamName some description
|
||||
* blah
|
||||
*/
|
||||
public void method_multiline_correct(String a, String superLongParamName) {
|
||||
}
|
||||
|
||||
}
|
||||
54
paper-checkstyle/src/testData/java/testdata/NullabilityAnnotations.java
vendored
Normal file
54
paper-checkstyle/src/testData/java/testdata/NullabilityAnnotations.java
vendored
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
package testdata;
|
||||
|
||||
import org.jspecify.annotations.NonNull;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
public class NullabilityAnnotations {
|
||||
|
||||
public int method(final int param) { // primitive, no annotation
|
||||
return 0;
|
||||
}
|
||||
|
||||
public Integer method2(final Integer param) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public @Nullable String[] missingArrayAnnotation() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String @Nullable[] missingTypeAnnotation() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public int[] method3(final int[] param) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NullMarked
|
||||
public static final class InnerClass {
|
||||
// no annotations cause null marked
|
||||
public static String method(final String param) {
|
||||
return "";
|
||||
}
|
||||
|
||||
public static final class InnerInnerClass {
|
||||
}
|
||||
}
|
||||
|
||||
public void method1(final @NonNull Integer param) {
|
||||
}
|
||||
|
||||
public void innerClass(final NullabilityAnnotations.InnerClass param) {
|
||||
}
|
||||
|
||||
public void innerClassCorrect(final NullabilityAnnotations.@Nullable InnerClass param) {
|
||||
}
|
||||
|
||||
public void innerInnerClass(final NullabilityAnnotations.InnerClass.InnerInnerClass param) {
|
||||
}
|
||||
|
||||
public void innerInnerClassCorrect(final NullabilityAnnotations.InnerClass.@Nullable InnerInnerClass param) {
|
||||
}
|
||||
}
|
||||
73
paper-checkstyle/src/testData/java/testdata/imports/UnnecessaryFullyQualifiedImport.java
vendored
Normal file
73
paper-checkstyle/src/testData/java/testdata/imports/UnnecessaryFullyQualifiedImport.java
vendored
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
// @formatter:off
|
||||
package testdata.imports;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import testdata.imports.testclasses.subpkg.OtherClass;
|
||||
import java.lang.*;
|
||||
|
||||
@SuppressWarnings("ALL")
|
||||
public class UnnecessaryFullyQualifiedImport {
|
||||
|
||||
static {
|
||||
final Map<String, String> map = new HashMap<>();
|
||||
final Set<Map.Entry<String, String>> entries = map.entrySet();
|
||||
final OtherClass otherClass = new OtherClass();
|
||||
final OtherClass.InnerClass innerClass = new OtherClass.InnerClass();
|
||||
final testdata.imports.testclasses.OtherClass otherClass1 = new testdata.imports.testclasses.OtherClass();
|
||||
final testdata.imports.testclasses.OtherClass.InnerClass innerClass1 = new testdata.imports.testclasses.OtherClass.InnerClass();
|
||||
final testdata.imports.testclasses.subpkg.OtherClass otherClass1_bad = new testdata.imports.testclasses.subpkg.OtherClass();
|
||||
final testdata.imports.testclasses.subpkg.OtherClass.InnerClass innerClass1_bad = new testdata.imports.testclasses.subpkg.OtherClass.InnerClass();
|
||||
|
||||
final Map<testdata.imports.testclasses.OtherClass.InnerClass, OtherClass.InnerClass> map1 = new HashMap<>();
|
||||
final Map<testdata.imports.testclasses.OtherClass.InnerClass, OtherClass> map2 = new HashMap<>();
|
||||
final Map<? extends testdata.imports.testclasses.OtherClass.InnerClass, ? extends OtherClass.InnerClass> map3 = new HashMap<>();
|
||||
final Map<? super testdata.imports.testclasses.OtherClass.InnerClass, ? extends OtherClass> map4 = new HashMap<>();
|
||||
}
|
||||
|
||||
public <A extends testdata.imports.testclasses.subpkg.OtherClass.InnerClass> A m(final Map<? super A, ? super testdata.imports.testclasses.subpkg.OtherClass.InnerClass> m) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public <A extends OtherClass.InnerClass> A m_correct(final Map<? super A, ? super OtherClass.InnerClass> m) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public <A extends testdata.imports.testclasses.subpkg.OtherClass.@org.jspecify.annotations.Nullable InnerClass> A m2(final Map<? super A, testdata.imports.testclasses.subpkg.OtherClass.InnerClass> m) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public <A extends OtherClass.InnerClass> A m2_correct(final Map<? super A, OtherClass.InnerClass> m) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Object o() {
|
||||
return (OtherClass.InnerClass & testdata.imports.testclasses.subpkg.OtherClass.InnerInterface) null;
|
||||
}
|
||||
|
||||
public Object o2() {
|
||||
return (OtherClass & testdata.imports.testclasses.subpkg.OtherInterface) null;
|
||||
}
|
||||
|
||||
public void methods() {
|
||||
testdata.imports.testclasses.subpkg.OtherInterface.<testdata.imports.testclasses.subpkg.OtherInterface>test();
|
||||
testdata.imports.testclasses.subpkg.OtherInterface.test();
|
||||
}
|
||||
|
||||
public void methods_correct() {
|
||||
OtherClass.<OtherClass>test();
|
||||
new OtherClass().instance_test();
|
||||
|
||||
OtherClass oc = new OtherClass();
|
||||
oc.instance_test();
|
||||
}
|
||||
|
||||
testdata.imports.testclasses.subpkg.OtherClass v = null;
|
||||
testdata.imports.testclasses.OtherClass v2 = null;
|
||||
|
||||
public void clazz() {
|
||||
System.out.println(testdata.imports.testclasses.subpkg.OtherClass.class);
|
||||
System.out.println(OtherClass.class);
|
||||
}
|
||||
}
|
||||
12
paper-checkstyle/src/testData/java/testdata/imports/testclasses/OtherClass.java
vendored
Normal file
12
paper-checkstyle/src/testData/java/testdata/imports/testclasses/OtherClass.java
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
// @formatter:off
|
||||
package testdata.imports.testclasses;
|
||||
|
||||
@SuppressWarnings("ALL")
|
||||
public class OtherClass {
|
||||
|
||||
public static class InnerClass {
|
||||
}
|
||||
|
||||
public interface InnerInterface {
|
||||
}
|
||||
}
|
||||
4
paper-checkstyle/src/testData/java/testdata/imports/testclasses/OtherInterface.java
vendored
Normal file
4
paper-checkstyle/src/testData/java/testdata/imports/testclasses/OtherInterface.java
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
package testdata.imports.testclasses;
|
||||
|
||||
public interface OtherInterface {
|
||||
}
|
||||
14
paper-checkstyle/src/testData/java/testdata/imports/testclasses/subpkg/OtherClass.java
vendored
Normal file
14
paper-checkstyle/src/testData/java/testdata/imports/testclasses/subpkg/OtherClass.java
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
package testdata.imports.testclasses.subpkg;
|
||||
|
||||
public class OtherClass {
|
||||
|
||||
public static class InnerClass {
|
||||
}
|
||||
|
||||
public interface InnerInterface {
|
||||
}
|
||||
|
||||
public static void test() {}
|
||||
|
||||
public void instance_test() {}
|
||||
}
|
||||
7
paper-checkstyle/src/testData/java/testdata/imports/testclasses/subpkg/OtherInterface.java
vendored
Normal file
7
paper-checkstyle/src/testData/java/testdata/imports/testclasses/subpkg/OtherInterface.java
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
package testdata.imports.testclasses.subpkg;
|
||||
|
||||
public interface OtherInterface {
|
||||
|
||||
static <T> void test() {
|
||||
}
|
||||
}
|
||||
|
|
@ -11,17 +11,17 @@ plugins {
|
|||
|
||||
if (!file(".git").exists()) {
|
||||
val errorText = """
|
||||
|
||||
|
||||
=====================[ ERROR ]=====================
|
||||
The Paper project directory is not a properly cloned Git repository.
|
||||
|
||||
|
||||
In order to build Paper from source you must clone
|
||||
the Paper repository using Git, not download a code
|
||||
zip from GitHub.
|
||||
|
||||
|
||||
Built Paper jars are available for download at
|
||||
https://papermc.io/downloads/paper
|
||||
|
||||
|
||||
See https://github.com/PaperMC/Paper/blob/main/CONTRIBUTING.md
|
||||
for further information on building and modifying Paper.
|
||||
===================================================
|
||||
|
|
@ -36,6 +36,8 @@ for (name in listOf("paper-api", "paper-server")) {
|
|||
file(name).mkdirs()
|
||||
}
|
||||
|
||||
include("paper-checkstyle")
|
||||
|
||||
optionalInclude("test-plugin")
|
||||
optionalInclude("paper-generator")
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue