diff --git a/.checkstyle/suppressions.xml b/.checkstyle/suppressions.xml new file mode 100644 index 0000000000..64a13f6144 --- /dev/null +++ b/.checkstyle/suppressions.xml @@ -0,0 +1,6 @@ + + + + diff --git a/.checkstyle/type-use-annotations.txt b/.checkstyle/type-use-annotations.txt new file mode 100644 index 0000000000..17d70deaa7 --- /dev/null +++ b/.checkstyle/type-use-annotations.txt @@ -0,0 +1,9 @@ +NonNull +NotNull +Nullable +UnknownNullability +Unmodifiable +UnmodifiableView +Range +Positive +NonNegative diff --git a/.checkstyle/xpath-suppressions.xml b/.checkstyle/xpath-suppressions.xml new file mode 100644 index 0000000000..b0ba3f631e --- /dev/null +++ b/.checkstyle/xpath-suppressions.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + diff --git a/.editorconfig b/.editorconfig index 600eac0fb9..4911a3847a 100644 --- a/.editorconfig +++ b/.editorconfig @@ -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 diff --git a/build.gradle.kts b/build.gradle.kts index a732f7ed3e..ad7ee97e50 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,8 +1,10 @@ +import io.papermc.paperweight.checkstyle.PaperCheckstyleExt +import io.papermc.paperweight.checkstyle.PaperCheckstyleTask import org.gradle.api.tasks.testing.logging.TestExceptionFormat import org.gradle.api.tasks.testing.logging.TestLogEvent plugins { - id("io.papermc.paperweight.core") version "2.0.0-beta.18" apply false + id("io.papermc.paperweight.core") version "2.0.0-SNAPSHOT" apply false } subprojects { @@ -19,6 +21,27 @@ subprojects { isPreserveFileTimestamps = false isReproducibleFileOrder = true } + + val tempDisabled = setOf("paper-server", "paper-generator", "test-plugin") + + if (name !in tempDisabled) { + apply(plugin = "io.papermc.paperweight.paper-checkstyle") + extensions.configure { + val typeUseAnnotationsProvider = providers + .fileContents(rootProject.layout.projectDirectory.file(".checkstyle/type-use-annotations.txt")) + .asText.map { it.trim().split("\n").toSet() } + typeUseAnnotations.set(typeUseAnnotationsProvider) + } + + tasks.withType().configureEach { + configDirectory = rootProject.layout.projectDirectory.dir(".checkstyle") + configFile = layout.projectDirectory.file(".checkstyle/checkstyle.xml").asFile + } + + dependencies { + "checkstyle"(project(":paper-checkstyle")) + } + } } val paperMavenPublicUrl = "https://repo.papermc.io/repository/maven-public/" diff --git a/paper-api/.checkstyle/checkstyle.xml b/paper-api/.checkstyle/checkstyle.xml new file mode 100644 index 0000000000..6e7485941e --- /dev/null +++ b/paper-api/.checkstyle/checkstyle.xml @@ -0,0 +1,219 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/paper-api/.checkstyle/packages.txt b/paper-api/.checkstyle/packages.txt new file mode 100644 index 0000000000..385cf915e8 --- /dev/null +++ b/paper-api/.checkstyle/packages.txt @@ -0,0 +1,198 @@ +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/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/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/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/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/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/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/ +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/ diff --git a/paper-api/build.gradle.kts b/paper-api/build.gradle.kts index 707bdbf0cb..3e4f50c204 100644 --- a/paper-api/build.gradle.kts +++ b/paper-api/build.gradle.kts @@ -1,3 +1,7 @@ +import io.papermc.paperweight.checkstyle.JavadocTag +import io.papermc.paperweight.checkstyle.PaperCheckstyleTask +import io.papermc.paperweight.checkstyle.setCustomJavadocTags + plugins { `java-library` `maven-publish` @@ -9,6 +13,22 @@ java { withJavadocJar() } +val customJavadocTags = setOf( + JavadocTag("apiNote", "a", "API Note:"), +) + +paperCheckstyle { + val packagesToSkipSource = providers + .fileContents(layout.projectDirectory.file(".checkstyle/packages.txt")) + .asText.map { it.trim().split("\n").toSet() } + + directoriesToSkip.set(packagesToSkipSource) +} + +tasks.withType().configureEach { + setCustomJavadocTags(customJavadocTags) +} + val annotationsVersion = "26.0.2" // Keep in sync with paper-server adventure-text-serializer-ansi dep val adventureVersion = "4.23.0" @@ -187,7 +207,7 @@ tasks.withType().configureEach { "https://logging.apache.org/log4j/2.x/javadoc/log4j-api/", "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 diff --git a/paper-api/src/main/java/io/papermc/paper/InternalAPIBridge.java b/paper-api/src/main/java/io/papermc/paper/InternalAPIBridge.java index 65e36495bd..98d6967f61 100644 --- a/paper-api/src/main/java/io/papermc/paper/InternalAPIBridge.java +++ b/paper-api/src/main/java/io/papermc/paper/InternalAPIBridge.java @@ -3,17 +3,15 @@ package io.papermc.paper; import io.papermc.paper.command.brigadier.CommandSourceStack; import io.papermc.paper.world.damagesource.CombatEntry; import io.papermc.paper.world.damagesource.FallLocationType; +import java.util.function.Predicate; import net.kyori.adventure.util.Services; import org.bukkit.block.Biome; import org.bukkit.damage.DamageEffect; import org.bukkit.damage.DamageSource; import org.bukkit.entity.LivingEntity; import org.jetbrains.annotations.ApiStatus; -import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; -import java.util.function.Predicate; - /** * Static bridge to the server internals. *

@@ -21,7 +19,6 @@ import java.util.function.Predicate; * cause issues when called under unexpected circumstances. */ @ApiStatus.Internal -@NullMarked public interface InternalAPIBridge { /** @@ -31,6 +28,7 @@ public interface InternalAPIBridge { */ static InternalAPIBridge get() { class Holder { + public static final InternalAPIBridge INSTANCE = Services.service(InternalAPIBridge.class).orElseThrow(); } @@ -49,30 +47,31 @@ public interface InternalAPIBridge { * Constructs the legacy custom biome instance for the biome enum. * * @return the created biome. + * @deprecated for removal, legacy custom biome constant isn't supported */ @Deprecated(forRemoval = true, since = "1.21.5") @ApiStatus.ScheduledForRemoval(inVersion = "1.22") Biome constructLegacyCustomBiome(); - + /** * Creates a new combat entry. *

* The fall location and fall distance will be calculated from the entity's current state. * - * @param entity entity + * @param entity entity * @param damageSource damage source - * @param damage damage amount + * @param damage damage amount * @return new combat entry */ CombatEntry createCombatEntry(LivingEntity entity, DamageSource damageSource, float damage); /** - * Creates a new combat entry + * Creates a new combat entry. * - * @param damageSource damage source - * @param damage damage amount + * @param damageSource damage source + * @param damage damage amount * @param fallLocationType fall location type - * @param fallDistance fall distance + * @param fallDistance fall distance * @return combat entry */ CombatEntry createCombatEntry(DamageSource damageSource, float damage, @Nullable FallLocationType fallLocationType, float fallDistance); @@ -87,4 +86,3 @@ public interface InternalAPIBridge { */ Predicate restricted(Predicate predicate); } - diff --git a/paper-api/src/main/java/io/papermc/paper/ServerBuildInfo.java b/paper-api/src/main/java/io/papermc/paper/ServerBuildInfo.java index 652ff54e7c..807fc00446 100644 --- a/paper-api/src/main/java/io/papermc/paper/ServerBuildInfo.java +++ b/paper-api/src/main/java/io/papermc/paper/ServerBuildInfo.java @@ -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 INSTANCE = Services.service(ServerBuildInfo.class); } // + 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. diff --git a/paper-api/src/main/java/io/papermc/paper/package-info.java b/paper-api/src/main/java/io/papermc/paper/package-info.java new file mode 100644 index 0000000000..c374169c56 --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/package-info.java @@ -0,0 +1,7 @@ +/** + * Root package for the Paper API. + */ +@NullMarked +package io.papermc.paper; + +import org.jspecify.annotations.NullMarked; diff --git a/paper-checkstyle/.checkstyle/checkstyle.xml b/paper-checkstyle/.checkstyle/checkstyle.xml new file mode 100644 index 0000000000..8a1e4fce56 --- /dev/null +++ b/paper-checkstyle/.checkstyle/checkstyle.xml @@ -0,0 +1,206 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/paper-checkstyle/build.gradle.kts b/paper-checkstyle/build.gradle.kts new file mode 100644 index 0000000000..a61e0fe3b0 --- /dev/null +++ b/paper-checkstyle/build.gradle.kts @@ -0,0 +1,8 @@ +plugins { + java +} + +dependencies { + implementation("com.puppycrawl.tools:checkstyle:10.26.1") + implementation("org.jspecify:jspecify:1.0.0") +} diff --git a/paper-checkstyle/src/main/java/io/papermc/checkstyle/Util.java b/paper-checkstyle/src/main/java/io/papermc/checkstyle/Util.java new file mode 100644 index 0000000000..af4ec85029 --- /dev/null +++ b/paper-checkstyle/src/main/java/io/papermc/checkstyle/Util.java @@ -0,0 +1,129 @@ +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.JavadocUtil; +import com.puppycrawl.tools.checkstyle.utils.TokenUtil; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.function.Predicate; +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 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 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); + for (final DetailAST annotation : Util.childrenIterator(annotations, TokenTypes.ANNOTATION)) { + if (annotationPredicate.test(annotation)) { + return true; + } + } + return false; + } +} diff --git a/paper-checkstyle/src/main/java/io/papermc/checkstyle/checks/JavadocAlignParameterDescriptionCheck.java b/paper-checkstyle/src/main/java/io/papermc/checkstyle/checks/JavadocAlignParameterDescriptionCheck.java new file mode 100644 index 0000000000..e5f8efdff9 --- /dev/null +++ b/paper-checkstyle/src/main/java/io/papermc/checkstyle/checks/JavadocAlignParameterDescriptionCheck.java @@ -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 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) + ); + } + } + } +} diff --git a/paper-checkstyle/src/main/java/io/papermc/checkstyle/checks/NullabilityAnnotationsCheck.java b/paper-checkstyle/src/main/java/io/papermc/checkstyle/checks/NullabilityAnnotationsCheck.java new file mode 100644 index 0000000000..f1d8ea28a8 --- /dev/null +++ b/paper-checkstyle/src/main/java/io/papermc/checkstyle/checks/NullabilityAnnotationsCheck.java @@ -0,0 +1,116 @@ +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; +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 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"); + } + } + final DetailAST dot = type.findFirstToken(TokenTypes.DOT); + final DetailAST annotationHolder; + final DetailAST identLoc; + if (dot != null) { + annotationHolder = dot.findFirstToken(TokenTypes.ANNOTATIONS); + identLoc = dot.findFirstToken(TokenTypes.IDENT); + } else { + annotationHolder = holderDef.findFirstToken(baseAnnotationHolderType); + identLoc = type; + } + if (hasNoNullabilityAnnotationChildren(annotationHolder)) { + this.log(identLoc.getLineNo(), identLoc.getColumnNo() - 1, "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); + } + } +} diff --git a/paper-checkstyle/src/main/java/io/papermc/checkstyle/checks/RedundantNullabilityCheck.java b/paper-checkstyle/src/main/java/io/papermc/checkstyle/checks/RedundantNullabilityCheck.java new file mode 100644 index 0000000000..1ed6c69551 --- /dev/null +++ b/paper-checkstyle/src/main/java/io/papermc/checkstyle/checks/RedundantNullabilityCheck.java @@ -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"); + } + } +} diff --git a/paper-checkstyle/src/main/java/io/papermc/checkstyle/checks/package-info.java b/paper-checkstyle/src/main/java/io/papermc/checkstyle/checks/package-info.java new file mode 100644 index 0000000000..f33ebfddfa --- /dev/null +++ b/paper-checkstyle/src/main/java/io/papermc/checkstyle/checks/package-info.java @@ -0,0 +1,8 @@ +/** + * Custom checkstyle checks for PaperMC projects. + */ +@NullMarked +@SuppressWarnings("unused") +package io.papermc.checkstyle.checks; + +import org.jspecify.annotations.NullMarked; diff --git a/paper-checkstyle/src/main/java/io/papermc/checkstyle/package-info.java b/paper-checkstyle/src/main/java/io/papermc/checkstyle/package-info.java new file mode 100644 index 0000000000..cea2de0c77 --- /dev/null +++ b/paper-checkstyle/src/main/java/io/papermc/checkstyle/package-info.java @@ -0,0 +1,7 @@ +/** + * This package contains custom checkstyle rules for PaperMC projects. + */ +@NullMarked +package io.papermc.checkstyle; + +import org.jspecify.annotations.NullMarked; diff --git a/paper-checkstyle/src/main/resources/checkstyle_packages.xml b/paper-checkstyle/src/main/resources/checkstyle_packages.xml new file mode 100644 index 0000000000..702587110a --- /dev/null +++ b/paper-checkstyle/src/main/resources/checkstyle_packages.xml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/paper-generator/.checkstyle/checkstyle.xml b/paper-generator/.checkstyle/checkstyle.xml new file mode 100644 index 0000000000..8a1e4fce56 --- /dev/null +++ b/paper-generator/.checkstyle/checkstyle.xml @@ -0,0 +1,206 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/settings.gradle.kts b/settings.gradle.kts index 9289c95a73..947a2e0a7b 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,5 +1,6 @@ pluginManagement { repositories { + mavenLocal() gradlePluginPortal() maven("https://repo.papermc.io/repository/maven-public/") } @@ -11,17 +12,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 +37,8 @@ for (name in listOf("paper-api", "paper-server")) { file(name).mkdirs() } +include("paper-checkstyle") + optionalInclude("test-plugin") optionalInclude("paper-generator")