LoginSignup
0
0

More than 3 years have passed since last update.

Gradle Core Plugin PMDの設定を考える。

Last updated at Posted at 2020-02-21

Target

  • GradleのPMDプラグインに何を記載すればいいのかを知りたい。
  • PMDの概要が知りたい。

Target version

Gradle 6.2
PMD 6.21.0

PMD Project

https://pmd.github.io/

PMD Source(GitHub)

https://github.com/pmd/pmd

PMD About

https://pmd.github.io/#about

-> Overview

PMDは、ソースコードの解析を行うプログラム。
プラグラミングをする上で、使用されていない変数や、オブジェクト、空ブロックを検出することができる。
コピーコードの検出ができる。

TLDR

以下の設定をすると良いよ。

// - non comments
// Use Gradle Command
// gradle check

plugins {
    id 'java'
    id 'pmd'
}

pmd {
    toolVersion '6.21.0'
    ignoreFailures = true
    consoleOutput = true
     ruleSets = [
        'category/java/bestpractices.xml',
        'category/java/codestyle.xml',
        'category/java/design.xml/AbstractClassWithoutAnyMethod',
        'category/java/design.xml/AvoidCatchingGenericException',
        'category/java/design.xml/AvoidDeeplyNestedIfStmts',
        'category/java/design.xml/AvoidRethrowingException',
        'category/java/design.xml/AvoidThrowingNewInstanceOfSameException',
        'category/java/design.xml/AvoidThrowingNullPointerException',
        'category/java/design.xml/AvoidThrowingRawExceptionTypes',
        'category/java/design.xml/AvoidUncheckedExceptionsInSignatures',
        'category/java/design.xml/ClassWithOnlyPrivateConstructorsShouldBeFinal',
        'category/java/design.xml/CollapsibleIfStatements',
        'category/java/design.xml/CouplingBetweenObjects',
        'category/java/design.xml/CyclomaticComplexity',
        'category/java/design.xml/DataClass',
        'category/java/design.xml/DoNotExtendJavaLangError',
        'category/java/design.xml/ExceptionAsFlowControl',
        'category/java/design.xml/ExcessiveClassLength',
        'category/java/design.xml/ExcessiveImports',
        'category/java/design.xml/ExcessiveMethodLength',
        'category/java/design.xml/ExcessiveParameterList',
        'category/java/design.xml/ExcessivePublicCount',
        'category/java/design.xml/FinalFieldCouldBeStatic',
        'category/java/design.xml/GodClass',
        'category/java/design.xml/ImmutableField',
        'category/java/design.xml/LawOfDemeter',
        'category/java/design.xml/LogicInversion',
        'category/java/design.xml/ModifiedCyclomaticComplexity',
        'category/java/design.xml/NcssConstructorCount',
        'category/java/design.xml/NcssCount',
        'category/java/design.xml/NcssMethodCount',
        'category/java/design.xml/NcssTypeCount',
        'category/java/design.xml/NPathComplexity',
        'category/java/design.xml/SignatureDeclareThrowsException',
        'category/java/design.xml/SimplifiedTernary',
        'category/java/design.xml/SimplifyBooleanAssertion',
        'category/java/design.xml/SimplifyBooleanExpressions',
        'category/java/design.xml/SimplifyBooleanReturns',
        'category/java/design.xml/SimplifyConditional',
        'category/java/design.xml/SingularField',
        'category/java/design.xml/StdCyclomaticComplexity',
        'category/java/design.xml/SwitchDensity',
        'category/java/design.xml/TooManyFields',
        'category/java/design.xml/TooManyMethods',
        'category/java/design.xml/UselessOverridingMethod',
        'category/java/design.xml/UseObjectForClearerAPI',
        'category/java/design.xml/UseUtilityClass',
        'category/java/documentation.xml',
        'category/java/errorprone.xml',
        'category/java/multithreading.xml',
        'category/java/performance.xml',
        'category/java/security.xml'
    ]
}

もう少し読める時間がある人はこちら。


// Use Gradle Command
// gradle check
// gradle pmdMain... target src/java/main/
// gradle pmdTest... target src/java/test/

plugins {
    id 'java' // Add Task check
    id 'pmd' // https://docs.gradle.org/current/userguide/pmd_plugin.html#sec:pmd_usage
}

// PMD設定  
// https://docs.gradle.org/current/dsl/org.gradle.api.plugins.quality.PmdExtension.html
pmd {
    toolVersion '6.21.0'    // 使用するPMDのバージョンを指定 ex) X.XX.X
    // https://docs.gradle.org/current/dsl/org.gradle.api.plugins.quality.PmdExtension.html#org.gradle.api.plugins.quality.PmdExtension:toolVersion
    // https://github.com/gradle/gradle/blob/a9f9b343a3ebfa34534d8245908a1f5702271ac9/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/PmdPlugin.java#L51
    ignoreFailures = true  // 警告が出てもビルド継続する
    // https://docs.gradle.org/current/dsl/org.gradle.api.plugins.quality.PmdExtension.html#org.gradle.api.plugins.quality.PmdExtension:ignoreFailures
    // https://github.com/gradle/gradle/blob/a9f9b343a3ebfa34534d8245908a1f5702271ac9/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/PmdPlugin.java#L133
    consoleOutput = true   // 解析結果を標準出力する。 default) false
    // https://docs.gradle.org/current/dsl/org.gradle.api.plugins.quality.PmdExtension.html#org.gradle.api.plugins.quality.PmdExtension:consoleOutput
    // https://github.com/gradle/gradle/blob/a9f9b343a3ebfa34534d8245908a1f5702271ac9/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/PmdPlugin.java#L145
    // reportsDir
    // explain) default output build/reports/pmd/
    // main.html ブラウザで見れるレポート
    // main.xml jenkinsなどで統計を取るためのXMLデータ

    // rulePriority
    // explain) default 5 全てのルールをチェックしてレポートを作成する。 優先度のレベルと例示のルールは ### PMD Priority に記載
    // memo) プロジェクトで扱っているライブラリやコーディングルールに沿わない場合は、優先度を下げることは可能。
    // https://docs.gradle.org/current/dsl/org.gradle.api.plugins.quality.PmdExtension.html#org.gradle.api.plugins.quality.PmdExtension:rulePriority
    // https://github.com/gradle/gradle/blob/88f0071392fbe1d5282a70d7ff641e899336f38f/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/PmdExtension.java#L40

    ruleSets = [
        'category/java/bestpractices.xml',
        'category/java/codestyle.xml',
        // design.xml
        'category/java/design.xml/AbstractClassWithoutAnyMethod',
        'category/java/design.xml/AvoidCatchingGenericException',
        'category/java/design.xml/AvoidDeeplyNestedIfStmts',
        'category/java/design.xml/AvoidRethrowingException',
        'category/java/design.xml/AvoidThrowingNewInstanceOfSameException',
        'category/java/design.xml/AvoidThrowingNullPointerException',
        'category/java/design.xml/AvoidThrowingRawExceptionTypes',
        'category/java/design.xml/AvoidUncheckedExceptionsInSignatures',
        'category/java/design.xml/ClassWithOnlyPrivateConstructorsShouldBeFinal',
        'category/java/design.xml/CollapsibleIfStatements',
        'category/java/design.xml/CouplingBetweenObjects',
        'category/java/design.xml/CyclomaticComplexity',
        'category/java/design.xml/DataClass',
        'category/java/design.xml/DoNotExtendJavaLangError',
        'category/java/design.xml/ExceptionAsFlowControl',
        'category/java/design.xml/ExcessiveClassLength',
        'category/java/design.xml/ExcessiveImports',
        'category/java/design.xml/ExcessiveMethodLength',
        'category/java/design.xml/ExcessiveParameterList',
        'category/java/design.xml/ExcessivePublicCount',
        'category/java/design.xml/FinalFieldCouldBeStatic',
        'category/java/design.xml/GodClass',
        'category/java/design.xml/ImmutableField',
        'category/java/design.xml/LawOfDemeter',
        'category/java/design.xml/LogicInversion',
        'category/java/design.xml/ModifiedCyclomaticComplexity',
        'category/java/design.xml/NcssConstructorCount',
        'category/java/design.xml/NcssCount',
        'category/java/design.xml/NcssMethodCount',
        'category/java/design.xml/NcssTypeCount',
        'category/java/design.xml/NPathComplexity',
        'category/java/design.xml/SignatureDeclareThrowsException',
        'category/java/design.xml/SimplifiedTernary',
        'category/java/design.xml/SimplifyBooleanAssertion',
        'category/java/design.xml/SimplifyBooleanExpressions',
        'category/java/design.xml/SimplifyBooleanReturns',
        'category/java/design.xml/SimplifyConditional',
        'category/java/design.xml/SingularField',
        'category/java/design.xml/StdCyclomaticComplexity',
        'category/java/design.xml/SwitchDensity',
        'category/java/design.xml/TooManyFields',
        'category/java/design.xml/TooManyMethods',
        'category/java/design.xml/UselessOverridingMethod',
        'category/java/design.xml/UseObjectForClearerAPI',
        'category/java/design.xml/UseUtilityClass',
        // remove category/java/design.xml/LoosePackageCoupling,
        'category/java/documentation.xml',
        'category/java/errorprone.xml',
        'category/java/multithreading.xml',
        'category/java/performance.xml',
        'category/java/security.xml'
    ]
    // https://docs.gradle.org/current/dsl/org.gradle.api.plugins.quality.PmdExtension.html#org.gradle.api.plugins.quality.PmdExtension:ruleSets
}

ここからは、調査、検証したことを記載していきます。

PmdExtension

Gradleのビルドインされているプラグインのことである。
PmdExtention.java

ルールセット、優先度に合わせて製品、テストコードへコンパイル前に検証を行う。

PMD Priority

ルールに優先度が存在している。
適用するレベルを設定することができる。

優先度 ドキュメントの表現 例示ルール
5 Low dataflowanomalyanalysis
4 Medium Low dontimportjavalang
3 Medium callsuperinconstructor
2 Medium High avoidusingnativecode
1 High classnamingconventions

PMD Plugin の概要については以下のドキュメンテーションを参照 :
https://docs.gradle.org/current/userguide/pmd_plugin.html

挙動を確認する。

実行されているログを見る。

> Task :pmdMain
Downloading https://jcenter.bintray.com/net/sourceforge/pmd/pmd-java/6.21.0/pmd-java-6.21.0.pom to /private/var/folders/_v/jzctdqqd1ddgz33jbrf1rz3c0000gn/T/gradle_download13671286225292861163bin
Downloading https://jcenter.bintray.com/net/sourceforge/pmd/pmd/6.21.0/pmd-6.21.0.pom to /private/var/folders/_v/jzctdqqd1ddgz33jbrf1rz3c0000gn/T/gradle_download5288879548247815093bin
Downloading https://jcenter.bintray.com/net/sourceforge/pmd/pmd-core/6.21.0/pmd-core-6.21.0.pom to /private/var/folders/_v/jzctdqqd1ddgz33jbrf1rz3c0000gn/T/gradle_download17762294337482693348bin
Downloading https://jcenter.bintray.com/net/sourceforge/pmd/pmd-core/6.21.0/pmd-core-6.21.0.jar to /private/var/folders/_v/jzctdqqd1ddgz33jbrf1rz3c0000gn/T/gradle_download1792798347815371543bin
Downloading https://jcenter.bintray.com/net/sourceforge/pmd/pmd-java/6.21.0/pmd-java-6.21.0.jar to /private/var/folders/_v/jzctdqqd1ddgz33jbrf1rz3c0000gn/T/gradle_download912273557516632649bin
Caching disabled for task ':pmdMain' because:
  Build cache is disabled
Task ':pmdMain' is not up-to-date because:
  Task has failed previously.
ビルドインのタスクはキャッシュされる。

Task 起動時に、対象 version のライブラリをダウンロードしてキャッシュをオフにしていると思われる。

repositories ブロックに記載しているリポジトリから、PMD の指定 version をダウンロードする。
複数のリポジトリが記載されている場合は不明(未検証)。

教訓

ローカルリポジトリやライブラリのダウンロード先に対象のversionのリソースがあるかを調査しておくこと。

ライブラリのダウンロード先
$HOME.gradle/caches/modules-2/files-2.1/net.sourceforge.pmd

# tree.
.
├── pmd
│   ├── 6.20.0
│   │   └── 153a58b9be815d96773d5fe7aea07f0cb15f151
│   │       └── pmd-6.20.0.pom
│   └── 6.21.0
│       └── 70e64070f6d533de54963432b9624c574500e9ec
│           └── pmd-6.21.0.pom
├── pmd-core
│   ├── 6.20.0
│   │   ├── 41c76ab6489085184c429c1996461981657fa8d
│   │   │   └── pmd-core-6.20.0.jar
│   │   └── 56e8ed051938d70aec5fdba77de4f2a4a78a94a0
│   │       └── pmd-core-6.20.0.pom
│   └── 6.21.0
│       ├── 64abdc6939f602a55018f4b702c7a241fde19e56
│       │   └── pmd-core-6.21.0.pom
│       └── 72ea7407d2c0b16a7f10211d7d880f05609498f8
│           └── pmd-core-6.21.0.jar
└── pmd-java
    ├── 6.20.0
    │   ├── 37bbce3dbe7fc208ad285859551ebf087a99a17f
    │   │   └── pmd-java-6.20.0.pom
    │   └── e4afac4b8ca4db0387503a96b0990131e8b494b7
    │       └── pmd-java-6.20.0.jar
    └── 6.21.0
        ├── 751f4eaf45d646e68552866a72d884b2b932c3ed
        │   └── pmd-java-6.21.0.pom
        └── d8732de1cad48084cf0c20436568f9cb64eee696
            └── pmd-java-6.21.0.jar

MEMO: 6.20.0があるのは、version指定をせずに実行した試行があったので、その時にダウンロードされたと思われる。

# cd pmd-core/6.21.0/51f4eaf45d646e68552866a72d884b2b932c3ed/pmd-java-6.21.0.pom
# cat pmd-java-6.21.0.pom

<resource>
    <directory>${basedir}/src/main/resources</directory>
    <filtering>true</filtering>
</resource>

# https://github.com/pmd/pmd/tree/pmd_releases/6.21.0/pmd-java/src/main/resources/category/java
  • ルールファイルは、キャッシュされているところから参照されている。

デフォルトで全てのルールは参照されるのだろうか。

キャッシュされたルールが適用されるのか検証

試行:ruleSets を未設定で実行した。

対象のソースコードはマスキングしている。実際は絶対パスで出力されている。


:pmdMain (Thread[Execution worker for ':',5,main]) started.

> Task :pmdMain
Caching disabled for task ':pmdMain' because:
  Build cache is disabled
Task ':pmdMain' is not up-to-date because:
  Value of input property 'ruleSets' has changed for task ':pmdMain'

dummy.java:11:  Field number has the same name as a method
dummy.java11:  Found non-transient, non-static member. Please mark as transient or provide accessors.
dummy.java.java:12:  Field name has the same name as a method
dummy.java.java:12:  Found non-transient, non-static member. Please mark as transient or provide accessors.
dummy.java.java:13:  Field mailAddress has the same name as a method
dummy.java.java:13:  Found non-transient, non-static member. Please mark as transient or provide accessors.
dummy.java.java:12:       Found non-transient, non-static member. Please mark as transient or provide accessors.
dummy.java.java:12:       It is somewhat confusing to have a field name matching the declaring class name
dummy.java.java:15:      Found non-transient, non-static member. Please mark as transient or provide accessors.
dummy.java.java:12:    Field value has the same name as a method
dummy.java.java:12:    Found non-transient, non-static member. Please mark as transient or provide accessors.

11 PMD rule violations were found. See the report at: file:///$HOME/TO/PROJECT_PATH/build/reports/pmd/main.html

:pmdMain (Thread[Execution worker for ':',5,main]) completed. Took 1.747 secs.

生成されたレポートの下記の場所のリンクにアクセスした。

dummy.java:11: Field number has the same name as a method

リンク先のルール: avoidfieldnamematchingmethodname
所属するルールセット: errorprone.xml

    // 一部省略
    Number number; // 違反していた箇所

    public Number number() {
        return number;
    }

違反内容はルールに対して該当していた。

<![CDATA[
public class Foo {
    Object bar;
    // bar is data or an action or both?
    void bar() {
    }
}
]]>

上記の該当箇所

PmdExtension-ruleSetsにデフォルト値の記載が言及されている

default ["category/java/errorprone.xml"]

試行結果
errorprone のみ有効化されている。

適用ルールをコントロールしたい場合

ruleSetsのプロパティに"category/java/errorprone.xml" の形式で設定すればいい。

このあたりのコミットからルールセットの場所が変更になった。

https://github.com/pmd/pmd/commit/68a56ee0d8d69f7cf2f925160f7fde9be45fd3de#diff-27673cb93291446a6a683fa21ff3b769

また、ここの箇所でversionの差異の判定をやっている。

設定できる項目は下記の表

XML PMD
bestpractices.xml best_practices.html
codestyle.xml codestyle.html
design.xml design.html
documentation.xml documentation.html
errorprone.xml errorprone.html
multithreading.xml multithreading.html
performance.xml performance.html
security.xml security.html

このプロパティに値が設定されていると、errorprone.xml のデフォルトは無効化されるので他の設定と抱き合わせるときは、設定すること。

他の設定をする際の例示はここで言及されている

ruleSets = ["category/java/errorprone.xml", "category/java/bestpractices.xml"]


試行:ruleSetsbestpractices.xml を設定して実行した。対象のソースコードはマスキングしている。実際は絶対パスで出力されている。


:pmdMain (Thread[Execution worker for ':' Thread 2,5,main]) started.

> Task :pmdMain
Caching disabled for task ':pmdMain' because:
  Build cache is disabled
Task ':pmdMain' is not up-to-date because:
  Task has failed previously.

dummy.java:5:   Avoid unused private fields such as 'value'.
dummy.java:7:   Avoid unused constructor parameters such as 'position'.

2 PMD rule violations were found. See the report at: file:///$HOME/TO/PROJECT_PATH/build/reports/pmd/main.html
:pmdMain (Thread[Execution worker for ':' Thread 2,5,main]) completed. Took 1.794 secs.

:compileTestJava (Thread[Execution worker for ':' Thread 2,5,main]) started.

生成されたレポートの下記の場所のリンクにアクセスした。

dummy.java:5: Avoid unused private fields such as 'value'.

リンク先のルール: unusedprivatefield
所属するルールセット: bestpractices.xml

    // 一部省略
    private String value; // 違反していた箇所

    public Position(String position) {

    }

違反内容はルールに対して該当していた。

<![CDATA[
public class Something {
    private static int FOO = 2; // Unused
    private int i = 5; // Unused
    private int j = 6;
    public int addOne() {
        return j++;
    }
}
]]>

https://github.com/pmd/pmd/blob/pmd_releases/6.21.0/pmd-java/src/main/resources/category/java/bestpractices.xml#L1373

default ["category/java/errorprone.xml"]

errorprone.xml は無効化されている。

全部適用した場合の検証
pmd {
    toolVersion '6.21.0'
    ignoreFailures = true
    consoleOutput = true
    ruleSets = [
        'category/java/bestpractices.xml',
        'category/java/codestyle.xml',
        'category/java/design.xml',
        'category/java/documentation.xml',
        'category/java/errorprone.xml',
        'category/java/multithreading.xml',
        'category/java/performance.xml',
        'category/java/security.xml'
    ]
}

試行:ruleSets にビルドインされているルールセットを適用する。対象のソースコードはマスキングしている。実際は絶対パスで出力されている。


:pmdMain (Thread[Execution worker for ':' Thread 3,5,main]) started.

> Task :pmdMain
Caching disabled for task ':pmdMain' because:
  Build cache is disabled

Task ':pmdMain' is not up-to-date because:
  Value of input property 'ruleSets' has changed for task ':pmdMain'

Removed misconfigured rule: LoosePackageCoupling  cause: No packages or classes specified
dummy.java:11:  Field comments are required

<<省略>>

dummyPositionStatus.java:16:    Public method and constructor comments are required
LoosePackageCoupling    -       No packages or classes specified
74 PMD rule violations were found. See the report at: file:///$HOME/TO/PROJECT_PATH/build/reports/pmd/main.html

:pmdMain (Thread[Execution worker for ':' Thread 3,5,main]) completed. Took 2.156 secs.

以下の項目が出力されている。見つからないということらしい。

Removed misconfigured rule: LoosePackageCoupling cause: No packages or classes specified

上記のルールを除外する方針で以下のことを実施する。

LoosePackageCoupling 以外を個別で適用する。ルールを除外するメソッドがない。
対象ルール
全46項目、そのうちのLoosePackageCouplingを削除する。

    ruleSets = [
        'category/java/bestpractices.xml',
        'category/java/codestyle.xml',
        'category/java/design.xml/AbstractClassWithoutAnyMethod',
        'category/java/design.xml/AvoidCatchingGenericException',
        'category/java/design.xml/AvoidDeeplyNestedIfStmts',
        'category/java/design.xml/AvoidRethrowingException',
        'category/java/design.xml/AvoidThrowingNewInstanceOfSameException',
        'category/java/design.xml/AvoidThrowingNullPointerException',
        'category/java/design.xml/AvoidThrowingRawExceptionTypes',
        'category/java/design.xml/AvoidUncheckedExceptionsInSignatures',
        'category/java/design.xml/ClassWithOnlyPrivateConstructorsShouldBeFinal',
        'category/java/design.xml/CollapsibleIfStatements',
        'category/java/design.xml/CouplingBetweenObjects',
        'category/java/design.xml/CyclomaticComplexity',
        'category/java/design.xml/DataClass',
        'category/java/design.xml/DoNotExtendJavaLangError',
        'category/java/design.xml/ExceptionAsFlowControl',
        'category/java/design.xml/ExcessiveClassLength',
        'category/java/design.xml/ExcessiveImports',
        'category/java/design.xml/ExcessiveMethodLength',
        'category/java/design.xml/ExcessiveParameterList',
        'category/java/design.xml/ExcessivePublicCount',
        'category/java/design.xml/FinalFieldCouldBeStatic',
        'category/java/design.xml/GodClass',
        'category/java/design.xml/ImmutableField',
        'category/java/design.xml/LawOfDemeter',
        'category/java/design.xml/LogicInversion',
        'category/java/design.xml/ModifiedCyclomaticComplexity',
        'category/java/design.xml/NcssConstructorCount',
        'category/java/design.xml/NcssCount',
        'category/java/design.xml/NcssMethodCount',
        'category/java/design.xml/NcssTypeCount',
        'category/java/design.xml/NPathComplexity',
        'category/java/design.xml/SignatureDeclareThrowsException',
        'category/java/design.xml/SimplifiedTernary',
        'category/java/design.xml/SimplifyBooleanAssertion',
        'category/java/design.xml/SimplifyBooleanExpressions',
        'category/java/design.xml/SimplifyBooleanReturns',
        'category/java/design.xml/SimplifyConditional',
        'category/java/design.xml/SingularField',
        'category/java/design.xml/StdCyclomaticComplexity',
        'category/java/design.xml/SwitchDensity',
        'category/java/design.xml/TooManyFields',
        'category/java/design.xml/TooManyMethods',
        'category/java/design.xml/UselessOverridingMethod',
        'category/java/design.xml/UseObjectForClearerAPI',
        'category/java/design.xml/UseUtilityClass',
        'category/java/documentation.xml',
        'category/java/errorprone.xml',
        'category/java/multithreading.xml',
        'category/java/performance.xml',
        'category/java/security.xml'
    ]

試行:ruleSets にビルドインされているルールセットからLoosePackageCoupling を除外して適用する。対象のソースコードはマスキングしている。実際は絶対パスで出力されている。



:pmdMain (Thread[Execution worker for ':' Thread 3,5,main]) started.

> Task :pmdMain
:pmdMain (Thread[Daemon worker Thread 9,5,main]) started.

<<省略>>

> Task :pmdMain
Caching disabled for task ':pmdMain' because:
  Build cache is disabled
Task ':pmdMain' is not up-to-date because:
  Value of input property 'ruleSets' has changed for task ':pmdMain'

<<省略>>

74 PMD rule violations were found. See the report at: file:///$HOME/TO/PROJECT_PATH/build/reports/pmd/main.html
:pmdMain (Thread[Daemon worker Thread 9,5,main]) completed. Took 2.338 secs.


参考にした資料

-gradle-
https://docs.gradle.org/current/userguide/userguide.html
https://docs.gradle.org/current/userguide/pmd_plugin.html
https://github.com/gradle/gradle/tree/v6.2.0

-pmd-
https://pmd.github.io/latest/index.html
https://pmd.github.io/latest/pmd_rules_java.html
https://github.com/pmd/pmd/tree/pmd_releases/6.21.0

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0