Compare commits

...
20 Commits
Author SHA1 Message Date
Jake Potrebic 64107df246 temporarily disable final local var check 2025-01-11 22:07:07 -08:00
Jake Potrebic 5e2d34c096 ok, fine. just use a proper gradle plugin 2025-01-11 18:57:23 -08:00
Jake Potrebic a0bec2312c move logic out of paper-api build.gradle.kts 2025-01-11 10:21:36 -08:00
Jake Potrebic 849d6107a9 remove unused runForAll 2025-01-07 00:14:27 -08:00
Jake Potrebic 1c35f64788 use checkstyle to create a much nicer nullability annotation test 2025-01-05 01:52:31 -08:00
Jake Potrebic cb005e4559 disable more warnings if root class is deprecated for removal 2025-01-04 23:22:51 -08:00
Jake Potrebic 08370f6416 enforce javadoc param description alignment 2025-01-04 22:13:28 -08:00
Jake Potrebic 6d981e0f10 simplify to just skip all packages until manually removed 2025-01-04 14:56:58 -08:00
Jake Potrebic ed5d703779 move to convention plugin 2025-01-04 13:40:29 -08:00
Jake Potrebic 7920bd0568 actually use file contents provider 2025-01-04 13:40:28 -08:00
Jake Potrebic 7a85e6cdfc fix for multiple source sets 2025-01-04 13:40:28 -08:00
Jake Potrebic d86abcf028 refactor a bunch to use task caching 2025-01-04 13:40:28 -08:00
Jake Potrebic d6c754a520 silence fetch 2025-01-04 13:40:28 -08:00
Jake Potrebic af3882662f setup github workflow 2025-01-04 13:40:28 -08:00
Jake Potrebic 8078445228 Naming and Whitespace
also only gets diff for commits that are on HEAD but not remote main
2025-01-04 13:40:28 -08:00
Jake Potrebic 3af3c9b773 only run on changed files for specific users 2025-01-04 13:40:28 -08:00
Jake Potrebic a95608398b Javadoc Comments, Misc and Modifiers 2025-01-04 13:40:28 -08:00
Jake Potrebic d7799ff7e8 Coding and Imports 2025-01-04 13:40:28 -08:00
Jake Potrebic 6c6e412974 remove redundant bit from include filter 2025-01-04 13:40:28 -08:00
Jake Potrebic b27cc5ddd4 beginning of checkstyle configuration for API 2025-01-04 13:40:28 -08:00
20 changed files with 809 additions and 384 deletions

No files matched your search

+16 -1
View File
@@ -8,6 +8,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
@@ -18,7 +19,7 @@ end_of_line=crlf
[*.yml]
indent_size=2
[*.patch]
[{*.patch,*.md}]
trim_trailing_whitespace=false
[*.java]
@@ -27,12 +28,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
+1 -1
View File
@@ -12,7 +12,7 @@ import java.nio.file.Path
import kotlin.random.Random
plugins {
id("io.papermc.paperweight.core") version "2.0.0-beta.10" apply false
id("io.papermc.paperweight.core") version "2.0.0-SNAPSHOT" apply false
}
subprojects {
+214
View File
@@ -0,0 +1,214 @@
<!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="SuppressionSingleFilter">
<!--Suppresses warnings except for common TYPE_USE that should be on same line-->
<property name="checks" value="AnnotationOnSameLine"/>
<property name="message" value="^Annotation '(?!(${type_use_annotations}))"/>
</module>
<module name="SuppressionSingleFilter">
<property name="checks" value="Javadoc(Method|Type)"/>
<!--[A-Z].* check is for java annotations inside code blocks inside javadocs-->
<property name="message" value="^Unknown tag '(${custom_javadoc_tags}|[A-Z].*)'.$"/>
</module>
<!--Javadoc Comments-->
<module name="JavadocPackage"/>
<!--Misc-->
<module name="NewlineAtEndOfFile"/>
<module name="OrderedProperties"/>
<!--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>
<!--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"/>
<module name="OneTopLevelClass"/>
<module name="SealedShouldHavePermitsList"/>
<!--Class Design-->
<module name="FinalClass"/>
<module name="HideUtilityClassConstructor"/>
<module name="InterfaceIsType"/>
<!--Coding-->
<module name="ArrayTrailingComma"/>
<module name="AvoidDoubleBraceInitialization"/>
<module name="AvoidNoArgumentSuperConstructorCall"/>
<module name="ConstructorsDeclarationGrouping"/>
<module name="CovariantEquals"/>
<module name="DeclarationOrder"/>
<module name="DefaultComesLast"/>
<module name="EmptyStatement"/>
<module name="EqualsAvoidNull"/>
<module name="EqualsHashCode"/>
<module name="FallThrough"/>
<!--<module name="FinalLocalVariable">
<property name="validateEnhancedForLoopVariable" value="true"/>
<property name="validateUnnamedVariables" value="true"/>
&lt;!&ndash;TODO atm, PARAMETER_DEF doesn't seem to function inside interfaces&ndash;&gt;
&lt;!&ndash;https://github.com/checkstyle/checkstyle/issues/16081&ndash;&gt;
<property name="tokens" value="PARAMETER_DEF,VARIABLE_DEF"/>
</module>-->
<module name="IllegalToken"/> <!--just labels by default-->
<module name="IllegalType"/>
<module name="RequireThis">
<property name="validateOnlyOverlapping" value="false"/>
</module>
<module name="SimplifyBooleanExpression"/>
<module name="SimplifyBooleanReturn"/>
<module name="StringLiteralEquality"/>
<module name="UnnecessarySemicolonAfterOuterTypeDeclaration"/>
<module name="UnnecessarySemicolonAfterTypeMemberDeclaration"/>
<module name="UnnecessarySemicolonInEnumeration"/>
<module name="UnnecessarySemicolonInTryWithResources"/>
<module name="UnusedCatchParameterShouldBeUnnamed"/>
<module name="UnusedLambdaParameterShouldBeUnnamed"/>
<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)"/>
<!--jetbrains nullability-->
<property name="illegalClasses" value="org\.jetbrains\.annotations\.(NotNull|Nullable|NotNullByDefault)"/>
<!--javax nullability-->
</module>
<module name="IllegalImport">
<property name="regexp" value="true"/>
<!--attempts to guard against nested imports (by looking for capital letters in imports)-->
<property name="illegalClasses" value="^[^\.A-Z]+(\.[^\.A-Z]+)+?(\.[A-Z][^\.]*)(\.[A-Z][^\.]*)+$"/>
<message key="import.illegal" value="Illegal nested import - {0}"/>
</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="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 name="NonEmptyAtclauseDescription"/>
<module name="RequireEmptyLineBeforeBlockTagGroup"/>
<!--Metrics--> <!--N/A-->
<!--Miscellaneous-->
<module name="ArrayTypeStyle"/>
<module name="AvoidEscapedUnicodeCharacters"/>
<module name="CommentsIndentation"/>
<module name="FinalParameters"/>
<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"/>
<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>
</module>
+182
View File
@@ -0,0 +1,182 @@
paper-api-generator/generated/com/destroystokyo/paper/entity/ai/
paper-api-generator/generated/io/papermc/paper/registry/keys/
paper-api-generator/generated/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/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/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/datacomponent/
paper-api/src/main/java/io/papermc/paper/datacomponent/item/
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/enchantments/
paper-api/src/main/java/io/papermc/paper/entity/
paper-api/src/main/java/io/papermc/paper/event/block/
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/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/math/
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/registry/
paper-api/src/main/java/io/papermc/paper/registry/data/
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/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/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/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/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/
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/entity/memory/
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/
+6
View File
@@ -0,0 +1,6 @@
<!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[^\\/]*[\\/].*"/>
</suppressions>
@@ -0,0 +1,9 @@
NonNull
NotNull
Nullable
UnknownNullability
Unmodifiable
UnmodifiableView
Range
Positive
NonNegative
@@ -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>
+23 -1
View File
@@ -1,7 +1,12 @@
import io.papermc.paperweight.checkstyle.JavadocTag
import io.papermc.paperweight.checkstyle.PaperCheckstyleTask
import io.papermc.paperweight.checkstyle.setCustomJavadocTags
plugins {
`java-library`
`maven-publish`
idea
id("io.papermc.paperweight.paper-checkstyle")
}
java {
@@ -9,6 +14,22 @@ java {
withJavadocJar()
}
val customJavadocTags = setOf(
JavadocTag("apiNote", "a", "API Note:"),
)
paperCheckstyle {
val packagesToSkipSource = providers.fileContents(projectLocalCheckstyleConfig.file("packages.txt")).asText.map { it.trim().split("\n").toSet() }
val typeUseAnnotationsProvider = providers.fileContents(projectLocalCheckstyleConfig.file("type-use-annotations.txt")).asText.map { it.trim().split("\n").toSet() }
directoriesToSkip.set(packagesToSkipSource)
typeUseAnnotations.set(typeUseAnnotationsProvider)
}
tasks.withType<PaperCheckstyleTask> {
setCustomJavadocTags(customJavadocTags)
}
val annotationsVersion = "26.0.1"
val bungeeCordChatVersion = "1.20-R0.2"
val adventureVersion = "4.18.0"
@@ -39,6 +60,7 @@ abstract class MockitoAgentProvider : CommandLineArgumentProvider {
}
dependencies {
checkstyle(project(":paper-api:custom-checkstyle"))
// api dependencies are listed transitively to API consumers
api("com.google.guava:guava:33.3.1-jre")
@@ -190,7 +212,7 @@ tasks.withType<Javadoc> {
"https://javadoc.io/doc/org.apache.logging.log4j/log4j-api/$log4jVersion/",
"https://javadoc.io/doc/org.apache.maven.resolver/maven-resolver-api/1.7.3",
)
options.tags("apiNote:a:API Note:")
options.tags(customJavadocTags.map { it.toOptionString() })
inputs.files(apiAndDocs).ignoreEmptyDirectories().withPropertyName(apiAndDocs.name + "-configuration")
val apiAndDocsElements = apiAndDocs.elements
@@ -0,0 +1,8 @@
plugins {
java
}
dependencies {
implementation("com.puppycrawl.tools:checkstyle:10.21.1")
implementation("org.jspecify:jspecify:1.0.0")
}
@@ -0,0 +1,90 @@
package io.papermc.checkstyle;
import com.puppycrawl.tools.checkstyle.api.DetailAST;
import com.puppycrawl.tools.checkstyle.api.DetailNode;
import com.puppycrawl.tools.checkstyle.utils.JavadocUtil;
import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
import java.util.Iterator;
import java.util.NoSuchElementException;
import org.jspecify.annotations.Nullable;
/**
* Utility class containing utility methods for custom checkstyle checks.
*/
public final class Util {
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 = JavadocUtil.getPreviousSibling(node);
while (sibling != null && sibling.getType() != type) {
sibling = JavadocUtil.getPreviousSibling(sibling);
}
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 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;
}
};
}
}
@@ -0,0 +1,49 @@
package io.papermc.checkstyle.checks;
import com.puppycrawl.tools.checkstyle.api.DetailNode;
import com.puppycrawl.tools.checkstyle.api.JavadocTokenTypes;
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;
/**
* Checks that parameter descriptions in Javadoc are aligned.
*/
public final class JavadocAlignParameterDescriptionCheck extends AbstractJavadocCheck {
@Override
public int[] getDefaultJavadocTokens() {
return new int[]{JavadocTokenTypes.JAVADOC};
}
@Override
public void visitJavadocToken(final DetailNode detailNode) {
final List<DetailNode> params = new ArrayList<>();
int maxColumn = -1;
for (final DetailNode child : detailNode.getChildren()) {
final DetailNode paramLiteralNode = JavadocUtil.findFirstToken(child, JavadocTokenTypes.PARAM_LITERAL);
if (child.getType() != JavadocTokenTypes.JAVADOC_TAG || paramLiteralNode == null) {
continue;
}
final DetailNode paramDescription = JavadocUtil.getNextSibling(paramLiteralNode, JavadocTokenTypes.DESCRIPTION);
maxColumn = Math.max(maxColumn, paramDescription.getColumnNumber());
params.add(paramDescription);
}
for (final DetailNode param : params) {
if (param.getColumnNumber() != maxColumn) {
final DetailNode paramNameNode = Util.getPreviousSibling(param, JavadocTokenTypes.PARAMETER_NAME);
if (paramNameNode == null) {
continue;
}
this.log(
param.getLineNumber(),
param.getColumnNumber() - 1,
"Param description for %s should start at column %d".formatted(paramNameNode.getText(), maxColumn)
);
}
}
}
}
@@ -0,0 +1,154 @@
package io.papermc.checkstyle.checks;
import com.puppycrawl.tools.checkstyle.JavaParser;
import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
import com.puppycrawl.tools.checkstyle.api.DetailAST;
import com.puppycrawl.tools.checkstyle.api.TokenTypes;
import io.papermc.checkstyle.Util;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Objects;
import java.util.Set;
import org.jspecify.annotations.Nullable;
/**
* Checks that nullability annotations are present where required.
*/
public final class NullabilityAnnotationsCheck extends AbstractCheck {
@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 final Set<String> NULLABILITY_ANNOTATIONS = Set.of("Nullable", "NonNull");
// for checking for annotations on nested types like "OuterType.@Nullable InnerType"
private static boolean typeHasNullabilityAnnotation(final @Nullable DetailAST type) {
final DetailAST dot;
if (type == null || (dot = type.findFirstToken(TokenTypes.DOT)) == null) {
return false;
}
return hasNullabilityAnnotationChild(dot.findFirstToken(TokenTypes.ANNOTATIONS));
}
private static boolean hasNullabilityAnnotationChild(final @Nullable DetailAST ast) {
if (ast == null) {
return false;
}
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 true;
}
}
return false;
}
private static boolean hasNullabilityAnnotation(final DetailAST inputAst) {
if (inputAst.getType() == TokenTypes.RECORD_COMPONENT_DEF && hasNullabilityAnnotationChild(inputAst.findFirstToken(TokenTypes.ANNOTATIONS))) {
return true;
} else if (inputAst.getType() != TokenTypes.RECORD_COMPONENT_DEF && hasNullabilityAnnotationChild(inputAst.findFirstToken(TokenTypes.MODIFIERS))) {
return true;
} else {
return typeHasNullabilityAnnotation(inputAst.findFirstToken(TokenTypes.TYPE));
}
}
private 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);
}
private static boolean isTypeDeclarationNullMarked(final DetailAST typeDeclaration) {
final DetailAST modifiers = typeDeclaration.findFirstToken(TokenTypes.MODIFIERS);
if (modifiers == null) {
return false;
}
for (final DetailAST annotation : Util.childrenIterator(modifiers, TokenTypes.ANNOTATION)) {
if (isNullMarkedAnnotation(annotation)) {
return true;
}
}
return false;
}
private boolean isPackageInfoNullMarked() {
final Path currentPath = Path.of(this.getFilePath());
final Path packageInfo = currentPath.getParent().resolve("package-info.java");
if (Files.notExists(packageInfo)) {
return false;
}
final DetailAST packageInfoAst;
try {
packageInfoAst = JavaParser.parseFile(packageInfo.toFile(), JavaParser.Options.WITHOUT_COMMENTS);
} catch (final IOException | CheckstyleException e) {
throw new RuntimeException(e);
}
final DetailAST firstToken = packageInfoAst.findFirstToken(TokenTypes.PACKAGE_DEF);
if (firstToken == null) {
return false;
}
final DetailAST annotations = firstToken.findFirstToken(TokenTypes.ANNOTATIONS);
for (final DetailAST annotation : Util.childrenIterator(annotations, TokenTypes.ANNOTATION)) {
if (isNullMarkedAnnotation(annotation)) {
return true;
}
}
return false;
}
@Override
public void visitToken(final DetailAST ast) {
if (hasNullabilityAnnotation(ast)) {
return;
}
for (DetailAST parentDef = Util.getEnclosingTypeDeclaration(ast); parentDef != null; parentDef = Util.getEnclosingTypeDeclaration(parentDef)) {
if (isTypeDeclarationNullMarked(parentDef)) {
return;
}
}
if (this.isPackageInfoNullMarked()) {
return;
}
this.recordViolation(ast);
}
private static DetailAST getTypeAstForViolation(final DetailAST inputAst) {
final DetailAST typeToken = inputAst.findFirstToken(TokenTypes.TYPE);
final DetailAST dot;
if (typeToken != null && (dot = typeToken.findFirstToken(TokenTypes.DOT)) != null) {
return Objects.requireNonNullElse(dot.getLastChild(), inputAst);
}
return Objects.requireNonNullElse(typeToken, inputAst);
}
private void recordViolation(final DetailAST ast) {
final DetailAST typeAst = getTypeAstForViolation(ast);
this.log(typeAst.getLineNo(), typeAst.getColumnNo() - 1, "Missing nullability annotation");
}
}
@@ -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,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>
@@ -3,7 +3,6 @@ package io.papermc.paper;
import net.kyori.adventure.util.Services;
import org.bukkit.damage.DamageEffect;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
/**
* Static bridge to the server internals.
@@ -12,7 +11,6 @@ import org.jspecify.annotations.NullMarked;
* cause issues when called under unexpected circumstances.
*/
@ApiStatus.Internal
@NullMarked
public interface InternalAPIBridge {
/**
@@ -36,4 +34,3 @@ public interface InternalAPIBridge {
*/
DamageEffect getDamageEffect(String key);
}
@@ -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();
}
@@ -47,7 +46,7 @@ public interface ServerBuildInfo {
* @return {@code true} if the server supports the specified brand
*/
@ApiStatus.Experimental
boolean isBrandCompatible(final Key brandId);
boolean isBrandCompatible(Key brandId);
/**
* Gets the brand name of the server.
@@ -104,7 +103,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.
@@ -0,0 +1,7 @@
/**
* Root package for the Paper API.
*/
@NullMarked
package io.papermc.paper;
import org.jspecify.annotations.NullMarked;
@@ -1,370 +0,0 @@
package org.bukkit;
import static org.junit.jupiter.api.Assertions.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.junit.jupiter.api.Test;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.ParameterNode;
public class AnnotationTest {
private static final String[] ACCEPTED_ANNOTATIONS = {
"Lorg/jetbrains/annotations/Nullable;",
"Lorg/jetbrains/annotations/NotNull;",
"Lorg/jetbrains/annotations/Contract;",
"Lorg/bukkit/UndefinedNullability;",
// Paper start
"Lorg/checkerframework/checker/nullness/qual/MonotonicNonNull;",
"Lorg/checkerframework/checker/nullness/qual/NonNull;",
"Lorg/checkerframework/checker/nullness/qual/Nullable;",
"Lorg/checkerframework/checker/nullness/qual/PolyNull;",
// Paper end
};
private static final String[] EXCLUDED_CLASSES = {
// Internal technical classes
"org/bukkit/plugin/java/JavaPluginLoader",
"org/bukkit/util/io/BukkitObjectInputStream",
"org/bukkit/util/io/BukkitObjectOutputStream",
"org/bukkit/util/io/Wrapper",
"org/bukkit/plugin/java/PluginClassLoader",
// Generic functional interface
"org/bukkit/util/Consumer",
// Paper start
"io/papermc/paper/util/TransformingRandomAccessList",
"io/papermc/paper/util/TransformingRandomAccessList$TransformedListIterator",
// Timings history is broken in terms of nullability due to guavas Function defining that the param is NonNull
"co/aikar/timings/TimingHistory$2",
"co/aikar/timings/TimingHistory$2$1",
"co/aikar/timings/TimingHistory$2$1$1",
"co/aikar/timings/TimingHistory$2$1$2",
"co/aikar/timings/TimingHistory$3",
"co/aikar/timings/TimingHistory$4",
"co/aikar/timings/TimingHistoryEntry$1"
// Paper end
};
@Test
public void testAll() throws IOException, URISyntaxException {
URL loc = Bukkit.class.getProtectionDomain().getCodeSource().getLocation();
File file = new File(loc.toURI());
// Running from jar is not supported yet
assertTrue(file.isDirectory(), "code must be in a directory");
final HashMap<String, ClassNode> foundClasses = new HashMap<>();
collectClasses(file, foundClasses);
final ArrayList<String> errors = new ArrayList<>();
for (ClassNode clazz : foundClasses.values()) {
if (!isClassIncluded(clazz, foundClasses)) {
continue;
}
// Paper start - skip class if it's @NullMarked
if (isClassNullMarked(clazz, foundClasses)) {
continue;
}
// Paper end - skip class if it's @NullMarked
for (MethodNode method : clazz.methods) {
if (!isMethodIncluded(clazz, method, foundClasses)) {
continue;
}
if (mustBeAnnotated(Type.getReturnType(method.desc)) && !isWellAnnotated(method.invisibleAnnotations)) {
// Paper start - Allow use of TYPE_USE annotations
boolean warn = true;
if (isWellAnnotated(method.visibleTypeAnnotations)) {
warn = false;
} else if (method.invisibleTypeAnnotations != null) {
dance: for (final org.objectweb.asm.tree.TypeAnnotationNode invisibleTypeAnnotation : method.invisibleTypeAnnotations) {
final org.objectweb.asm.TypeReference ref = new org.objectweb.asm.TypeReference(invisibleTypeAnnotation.typeRef);
if (ref.getSort() == org.objectweb.asm.TypeReference.METHOD_RETURN && java.util.Arrays.asList(ACCEPTED_ANNOTATIONS).contains(invisibleTypeAnnotation.desc)) {
warn = false;
break dance; // cha cha real smooth
}
}
}
if (warn)
// Paper end
warn(errors, clazz, method, "return value");
}
Type[] paramTypes = Type.getArgumentTypes(method.desc);
List<ParameterNode> parameters = method.parameters;
dancing: // Paper
for (int i = 0; i < paramTypes.length; i++) {
if (mustBeAnnotated(paramTypes[i]) ^ isWellAnnotated(method.invisibleParameterAnnotations == null ? null : method.invisibleParameterAnnotations[i])) {
// Paper start
if (method.invisibleTypeAnnotations != null) {
for (final org.objectweb.asm.tree.TypeAnnotationNode invisibleTypeAnnotation : method.invisibleTypeAnnotations) {
final org.objectweb.asm.TypeReference ref = new org.objectweb.asm.TypeReference(invisibleTypeAnnotation.typeRef);
if (ref.getSort() == org.objectweb.asm.TypeReference.METHOD_FORMAL_PARAMETER && ref.getTypeParameterIndex() == i && java.util.Arrays.asList(ACCEPTED_ANNOTATIONS).contains(invisibleTypeAnnotation.desc)) {
continue dancing;
}
}
}
if (method.visibleTypeAnnotations != null) {
for (final org.objectweb.asm.tree.TypeAnnotationNode invisibleTypeAnnotation : method.visibleTypeAnnotations) {
final org.objectweb.asm.TypeReference ref = new org.objectweb.asm.TypeReference(invisibleTypeAnnotation.typeRef);
if (ref.getSort() == org.objectweb.asm.TypeReference.METHOD_FORMAL_PARAMETER && ref.getTypeParameterIndex() == i && java.util.Arrays.asList(ACCEPTED_ANNOTATIONS).contains(invisibleTypeAnnotation.desc)) {
continue dancing;
}
}
}
// Paper end - Allow use of TYPE_USE annotations
ParameterNode paramNode = parameters == null ? null : parameters.get(i);
String paramName = paramNode == null ? null : paramNode.name;
warn(errors, clazz, method, "parameter " + i + (paramName == null ? "" : ": " + paramName));
}
}
}
}
if (errors.isEmpty()) {
// Success
return;
}
Collections.sort(errors);
StringBuilder builder = new StringBuilder()
.append("There ")
.append(errors.size() != 1 ? "are " : "is ")
.append(errors.size())
.append(" missing annotation")
.append(errors.size() != 1 ? "s:\n" : ":\n");
for (String message : errors) {
builder.append("\t").append(message).append("\n");
}
fail(builder.toString());
}
private static void collectClasses(@NotNull File from, @NotNull Map<String, ClassNode> to) throws IOException {
if (from.isDirectory()) {
// Paper start - skip packages with @NullMarked
final File packageInfo = new File(from, "package-info.class");
if (packageInfo.exists()) {
try (final FileInputStream in = new FileInputStream(packageInfo)) {
final ClassReader cr = new ClassReader(in);
final ClassNode node = new ClassNode();
cr.accept(node, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
if (isClassNullMarked0(node)) {
return; // skip packages with @NullMarked
}
}
}
// Paper end - skip packages with @NullMarked
final File[] files = from.listFiles();
assert files != null;
for (File file : files) {
collectClasses(file, to);
}
return;
}
if (!from.getName().endsWith(".class")) {
return;
}
try (FileInputStream in = new FileInputStream(from)) {
final ClassReader cr = new ClassReader(in);
final ClassNode node = new ClassNode();
cr.accept(node, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
to.put(node.name, node);
}
}
// Paper start - skip class if it's @NullMarked
private static boolean isClassNullMarked(@NotNull ClassNode clazz, @NotNull Map<String, ClassNode> allClasses) {
if (isClassNullMarked0(clazz)) {
return true;
}
if (clazz.nestHostClass != null) {
final ClassNode nestHostNode = allClasses.get(clazz.nestHostClass);
if (nestHostNode != null) {
return isClassNullMarked(nestHostNode, allClasses);
}
}
return false;
}
private static boolean isClassNullMarked0(@NotNull ClassNode clazz) {
return clazz.visibleAnnotations != null && clazz.visibleAnnotations.stream().anyMatch(node -> "Lorg/jspecify/annotations/NullMarked;".equals(node.desc));
}
// Paper end - skip class if it's @NullMarked
private static boolean isClassIncluded(@NotNull ClassNode clazz, @NotNull Map<String, ClassNode> allClasses) {
// Exclude private, synthetic or deprecated classes and annotations, since their members can't be null
if ((clazz.access & (Opcodes.ACC_PRIVATE | Opcodes.ACC_SYNTHETIC | Opcodes.ACC_DEPRECATED | Opcodes.ACC_ANNOTATION)) != 0) {
return false;
}
if (isSubclassOf(clazz, "org/bukkit/material/MaterialData", allClasses)) {
throw new AssertionError("Subclass of MaterialData must be deprecated: " + clazz.name);
}
if (isSubclassOf(clazz, "java/lang/Exception", allClasses)
|| isSubclassOf(clazz, "java/lang/RuntimeException", allClasses)) {
// Exceptions are excluded
return false;
}
// Paper start
if (isInternal(clazz.invisibleAnnotations)) {
return false;
}
// Paper end
for (String excludedClass : EXCLUDED_CLASSES) {
if (excludedClass.equals(clazz.name)) {
return false;
}
}
return true;
}
private static boolean isMethodIncluded(@NotNull ClassNode clazz, @NotNull MethodNode method, @NotNull Map<String, ClassNode> allClasses) {
// Exclude private, synthetic and deprecated methods
if ((method.access & (Opcodes.ACC_PRIVATE | Opcodes.ACC_SYNTHETIC | Opcodes.ACC_DEPRECATED)) != 0 || (method.access & (Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED | Opcodes.ACC_PUBLIC)) == 0) { // Paper - ignore package-private
return false;
}
// Exclude Java methods
if (is(method, "toString", 0) || is(method, "clone", 0) || is(method, "equals", 1)) {
return false;
}
// Exclude generated Enum methods
if (isSubclassOf(clazz, "java/lang/Enum", allClasses) && (is(method, "values", 0) || is(method, "valueOf", 1))) {
return false;
}
// Anonymous classes have generated constructors, which can't be annotated nor invoked
if ("<init>".equals(method.name) && isAnonymous(clazz)) {
return false;
}
// Paper start
if (isInternal(method.invisibleAnnotations)) {
return false;
}
// Paper end
return true;
}
// Paper start
private static boolean isInternal(List<? extends AnnotationNode> annotations) {
if (annotations == null) {
return false;
}
for (AnnotationNode node : annotations) {
if (node.desc.equals("Lorg/jetbrains/annotations/ApiStatus$Internal;")) {
return true;
}
}
return false;
}
// Paper end
private static boolean isWellAnnotated(@Nullable List<? extends AnnotationNode> annotations) { // Paper
if (annotations == null) {
return false;
}
for (AnnotationNode node : annotations) {
for (String acceptedAnnotation : ACCEPTED_ANNOTATIONS) {
if (acceptedAnnotation.equals(node.desc)) {
return true;
}
}
}
return false;
}
private static boolean mustBeAnnotated(@NotNull Type type) {
return type.getSort() == Type.ARRAY || type.getSort() == Type.OBJECT;
}
private static boolean is(@NotNull MethodNode method, @NotNull String name, int parameters) {
final List<ParameterNode> params = method.parameters;
return method.name.equals(name) && (params == null || params.size() == parameters);
}
/**
* Checks if the class is anonymous.
*
* @param clazz the class to check
* @return true if given class is anonymous
*/
private static boolean isAnonymous(@NotNull ClassNode clazz) {
final String name = clazz.name;
if (name == null) {
return false;
}
final int nestedSeparator = name.lastIndexOf('$');
if (nestedSeparator == -1 || nestedSeparator + 1 == name.length()) {
return false;
}
// Nested classes have purely numeric names. Java classes can't begin with a number,
// so if first character is a number, the class must be anonymous
final char c = name.charAt(nestedSeparator + 1);
return c >= '0' && c <= '9';
}
private static boolean isSubclassOf(@NotNull ClassNode what, @NotNull String ofWhat, @NotNull Map<String, ClassNode> allClasses) {
if (ofWhat.equals(what.name)
// Not only optimization: Super class may not be present in allClasses, so it is checked here
|| ofWhat.equals(what.superName)) {
return true;
}
final ClassNode parent = allClasses.get(what.superName);
if (parent != null && isSubclassOf(parent, ofWhat, allClasses)) {
return true;
}
for (String superInterface : what.interfaces) {
final ClassNode interfaceParent = allClasses.get(superInterface);
if (interfaceParent != null && isSubclassOf(interfaceParent, ofWhat, allClasses)) {
return true;
}
}
return false;
}
private static void warn(@NotNull Collection<String> out, @NotNull ClassNode clazz, @NotNull MethodNode method, @NotNull String description) {
out.add(clazz.name + " \t" + method.name + " \t" + description);
}
}
+6 -4
View File
@@ -2,6 +2,7 @@ import java.util.Locale
pluginManagement {
repositories {
mavenLocal()
gradlePluginPortal()
maven("https://repo.papermc.io/repository/maven-public/")
}
@@ -13,17 +14,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.
===================================================
@@ -42,6 +43,7 @@ for (name in listOf("paper-api", "paper-server")) {
optionalInclude("test-plugin")
optionalInclude("paper-api-generator")
include("paper-api:custom-checkstyle")
fun optionalInclude(name: String, op: (ProjectDescriptor.() -> Unit)? = null) {
val settingsFile = file("$name.settings.gradle.kts")