Target
- GradleのPMDプラグインに何を記載すればいいのかを知りたい。
- PMDの概要が知りたい。
Target version
PMD Project
PMD Source(GitHub)
PMD 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"
の形式で設定すればいい。
このあたりのコミットからルールセットの場所が変更になった。
設定できる項目は下記の表
このプロパティに値が設定されていると、errorprone.xml のデフォルトは無効化されるので他の設定と抱き合わせるときは、設定すること。
ruleSets = ["category/java/errorprone.xml", "category/java/bestpractices.xml"]
試行:ruleSets
にbestpractices.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++;
}
}
]]>
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