PMD
ルールセットを作成するにあたり、標準的なものを作ってみようと思いました。
あまり個人の思いをぶつけたものを作ると、仮に会社で適用するにあたり話が通らなそうであるので、
Github
上に公開されているものをベースに平均値的なものを作成してみます。
目次
サンプルの抽出
-
Github
のstar
数が100以上のプロジェクトを対象に抽出します。
100以上は、対象プロジェクト数が6177
で、数として勝手にちょうどよさそうに思ったからです。
ただ、repository search api
の仕様上、最大1000件までしか取得できなかったため、
API のソート項目を変えながら、取得可能だった5419プロジェクトを対象にしました。 -
上記のリポジトリからファイル名が、以下の名前と一致するものを抽出します。
(pom.xml
にruleset
定義が書いてあったりしますが、それは対象外…)pmd.xml
pmdrulesets.xml
pmdruleset.xml
pmd-ruleset.xml
pmd-rulesets.xml
集計方法
- ルール単位に集計します。
exclude
定義があるものはカウント対象外にします。 以下の場合は、rulesets/imports.xml
配下のルールはカウントするけれど、
TooManyStaticImports
は対象外にします。
<rule ref="rulesets/imports.xml">
<exclude name="TooManyStaticImports" />
</rule>
- ルールの全量は、PMD - PMD Rulesets index にのっています。
(pmd-4.3.0
で、pmd5
のルールセットではないですが大方あっているだろう。で上記から取得しました。)
集計に使用するGithub API
集計のため以下の、API を使用しました。
1. Githubから、リポジトリ検索 API でスター数 100 以上の Javaプロジェクトを検索
2. コード検索 APIで 1.
で取得したリポジトリから PMD の設定ファイルを検索
3. 2.
でヒットした PMD の設定ファイルを解析。使用ルールを集計
リポジトリ検索 API 使用 URL
- star が200以上の java プロジェクトを検索する
https://api.github.com/search/repositories?q=language:java+stars:>=200
- star が100以上、199以下の java プロジェクトを検索する
https://api.github.com/search/repositories?q=language:java+stars:100..199
コード検索 API URL
- repository から pmd の設定ファイルを検索する。
https://api.github.com/search/code?q=filename:pmd-rulesets.xml+filename:pmd.xml+filename:pmdruleset.xml+filename:pmdrulesets.xml+filename:pmd-ruleset.xml+repo:blueshen/ut-maven-plugin
PMD 設定ファイルの取得 ダウンロード URL
ファイルの実体は、https://raw.githubusercontent.com
配下に存在します。
コード検索 API の戻り に含まれるhtml_url
を以下のように置換することで、
実ファイルの URL を取得することができます。
python で 記述したところ以下のようになりました。
# convert row url
row_url = doc.get('html_url')
# URLを https://github.com から、https://raw.githubusercontent.comに置換
row_url = row_url.replace('https://github.com', 'https://raw.githubusercontent.com')
# ディレクトリ blob/ を削除
row_url = row_url.replace('blob/', '')
変換後の URL は以下になります。
この URL にアクセスすると、実ファイルが取得できます。
https://raw.githubusercontent.com/blueshen/ut-maven-plugin/dc371d859292cdad07cc35a31bc9403e0384f005/pmd-rulesets.xml
実装
実装は python
で行いました。
python のライブラリを検索したところ、github3.py 等幾つかありました。
しかし、何故か pip
は上手くいったにも関わらず import
エラーになったりしたため、
request
、json
を使って自前で実装しました。
集計結果
1. PMDルールファイルを保持しているプロジェクト数
- 81/5419 プロジェクト
個人的に10%くらいは、使っているのかと思ってましたが、
1.5パーセント程度でほとんど使われていないようです。
取得の仕方が悪かったのかもしれません。集計サンプル数として少ないかもですが、
とりあえず続けます。
2. 適用されているルールのTOP10
NO | ルール名 | カウント |
---|---|---|
1 | rulesets/java/basic.xml/BooleanInstantiation | 42 |
1 | rulesets/java/basic.xml/UnconditionalIfStatement | 42 |
3 | rulesets/java/strings.xml/UnnecessaryCaseChang | 41 |
3 | rulesets/java/strings.xml/StringToString | 41 |
3 | rulesets/java/strings.xml/StringInstantiatio | 41 |
6 | rulesets/java/strings.xml/InefficientStringBuffering | 40 |
6 | rulesets/java/basic.xml/CollapsibleIfStatement | 40 |
6 | rulesets/java/strings.xml/UseStringBufferLengt | 40 |
6 | rulesets/java/strings.xml/UseIndexOfChar | 40 |
6 | rulesets/java/strings.xml/StringBufferInstantiationWithChar | 40 |
6 | rulesets/java/basic.xml/DoubleCheckedLocking | 40 |
6 | rulesets/java/strings.xml/UselessStringValueOf | 40 |
basic
strings
ルールセットが多く使われているようです。
3. 適用除外ルールのTOP10
NO | ルール名 | カウント |
---|---|---|
1 | rulesets/java/naming.xml/LongVariable | -24 |
2 | rulesets/java/naming.xml/ShortVariable | -23 |
3 | rulesets/java/naming.xml/AbstractNaming | -20 |
4 | rulesets/java/imports.xml/TooManyStaticImports | -18 |
5 | rulesets/java/naming.xml/ShortMethodName | -16 |
6 | rulesets/java/naming.xml/VariableNamingConventions | -14 |
6 | rulesets/java/naming.xml/ShortClassName | -14 |
8 | rulesets/java/optimizations.xml/AvoidInstantiatingObjectsInLoops | -11 |
9 | rulesets/java/coupling.xml/LawOfDemeter | -10 |
10 | rulesets/java/optimizations.xml/LocalVariableCouldBeFinal | -8 |
10 | rulesets/java/controversial.xml/OnlyOneReturn | -8 |
10 | rulesets/java/controversial.xml/DataflowAnomalyAnalysis | -8 |
10 | rulesets/java/strings.xml/AvoidDuplicateLiterals | -8 |
10 | rulesets/java/controversial.xml/AtLeastOneConstructor | -8 |
Naming
ルールセット内のルールが除外指定が多く除外指定されています。
確かに準拠するのが難しい気がします。
4. 詳細
集計データの詳細( MongoDB
のCSV
エクスポートデータ )は、
こちらのGoogle スプレッドシート
を添付しました。
* 集計サマリ
* 除外集計サマリ
を見てもらえれば良いかと思います。
PMDルールファイル
集計結果を元に以下のロジックでルールファイルを作成します。
- ルールの集計数が平均値を超えるものをルール単位で、記述。
- 1.に含まれる場合でも、除外数が平均値を超えるものは除外。
作成したルールファイルは以下になります。
<ruleset name="Custom ruleset"
xmlns="http://pmd.sf.net/ruleset/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd"
xsi:noNamespaceSchemaLocation="http://pmd.sf.net/ruleset_xml_schema.xsd">
<description>
Github Collect Rule Set
</description>
<rule ref="rulesets/java/basic.xml/BooleanInstantiation"/>
<rule ref="rulesets/java/basic.xml/UnconditionalIfStatement"/>
<rule ref="rulesets/java/strings.xml/UnnecessaryCaseChange"/>
<rule ref="rulesets/java/strings.xml/StringToString"/>
<rule ref="rulesets/java/strings.xml/StringInstantiation"/>
<rule ref="rulesets/java/strings.xml/InefficientStringBuffering"/>
<rule ref="rulesets/java/basic.xml/CollapsibleIfStatements"/>
<rule ref="rulesets/java/strings.xml/UseStringBufferLength"/>
<rule ref="rulesets/java/strings.xml/UseIndexOfChar"/>
<rule ref="rulesets/java/strings.xml/StringBufferInstantiationWithChar"/>
<rule ref="rulesets/java/basic.xml/DoubleCheckedLocking"/>
<rule ref="rulesets/java/strings.xml/UselessStringValueOf"/>
<rule ref="rulesets/java/basic.xml/BrokenNullCheck"/>
<rule ref="rulesets/java/basic.xml/ClassCastExceptionWithToArray"/>
<rule ref="rulesets/java/basic.xml/AvoidDecimalLiteralsInBigDecimalConstructor"/>
<rule ref="rulesets/java/imports.xml/DontImportJavaLang"/>
<rule ref="rulesets/java/basic.xml/BigIntegerInstantiation"/>
<rule ref="rulesets/java/logging-java.xml/AvoidPrintStackTrace"/>
<rule ref="rulesets/java/basic.xml/ForLoopShouldBeWhileLoop"/>
<rule ref="rulesets/java/basic.xml/ReturnFromFinallyBlock"/>
<rule ref="rulesets/java/imports.xml/DuplicateImports"/>
<rule ref="rulesets/java/basic.xml/JumbledIncrementer"/>
<rule ref="rulesets/java/logging-java.xml/SystemPrintln"/>
<rule ref="rulesets/java/strings.xml/UseEqualsToCompareStrings"/>
<rule ref="rulesets/java/braces.xml/IfElseStmtsMustUseBraces"/>
<rule ref="rulesets/java/braces.xml/WhileLoopsMustUseBraces"/>
<rule ref="rulesets/java/strings.xml/AppendCharacterWithChar"/>
<rule ref="rulesets/java/braces.xml/ForLoopsMustUseBraces"/>
<rule ref="rulesets/java/strings.xml/InefficientEmptyStringCheck"/>
<rule ref="rulesets/java/clone.xml/CloneThrowsCloneNotSupportedException"/>
<rule ref="rulesets/java/strings.xml/AvoidStringBufferField"/>
<rule ref="rulesets/java/basic.xml/OverrideBothEqualsAndHashcode"/>
<rule ref="rulesets/java/basic.xml/MisplacedNullCheck"/>
<rule ref="rulesets/java/finalizers.xml/AvoidCallingFinalize"/>
<rule ref="rulesets/java/basic.xml/AvoidUsingOctalValues"/>
<rule ref="rulesets/java/basic.xml/CheckResultSet"/>
<rule ref="rulesets/java/naming.xml/ClassNamingConventions"/>
<rule ref="rulesets/java/basic.xml/AvoidThreadGroup"/>
<rule ref="rulesets/java/strings.xml/ConsecutiveLiteralAppends"/>
<rule ref="rulesets/java/basic.xml/AvoidMultipleUnaryOperators"/>
<rule ref="rulesets/java/strings.xml/InsufficientStringBufferDeclaration"/>
<rule ref="rulesets/java/logging-java.xml/LoggerIsNotStaticFinal"/>
<rule ref="rulesets/java/naming.xml/SuspiciousConstantFieldName"/>
<rule ref="rulesets/java/naming.xml/SuspiciousHashcodeMethodName"/>
<rule ref="rulesets/java/strings.xml/ConsecutiveAppendsShouldReuse"/>
<rule ref="rulesets/java/finalizers.xml/FinalizeDoesNotCallSuperFinalize"/>
<rule ref="rulesets/java/naming.xml/AvoidDollarSigns"/>
<rule ref="rulesets/java/imports.xml/ImportFromSamePackage"/>
<rule ref="rulesets/java/naming.xml/SuspiciousEqualsMethodName"/>
<rule ref="rulesets/java/basic.xml/DontCallThreadRun"/>
<rule ref="rulesets/java/basic.xml/DontUseFloatTypeForLoopIndices"/>
<rule ref="rulesets/java/finalizers.xml/FinalizeOverloaded"/>
<rule ref="rulesets/java/finalizers.xml/EmptyFinalizer"/>
<rule ref="rulesets/java/naming.xml/MethodWithSameNameAsEnclosingClass"/>
<rule ref="rulesets/java/basic.xml/CheckSkipResult"/>
<rule ref="rulesets/java/basic.xml/ExtendsObject"/>
<rule ref="rulesets/java/imports.xml/UnusedImports"/>
<rule ref="rulesets/java/logging-java.xml/MoreThanOneLogger"/>
<rule ref="rulesets/java/basic.xml/AvoidUsingHardCodedIP"/>
<rule ref="rulesets/java/basic.xml/AvoidBranchingStatementAsLastInLoop"/>
<rule ref="rulesets/java/imports.xml/UnnecessaryFullyQualifiedName"/>
<rule ref="rulesets/java/clone.xml/CloneMethodMustImplementCloneable"/>
<rule ref="rulesets/java/empty.xml/EmptyCatchBlock"/>
<rule ref="rulesets/java/clone.xml/ProperCloneImplementation"/>
<rule ref="rulesets/java/naming.xml/NoPackage"/>
<rule ref="rulesets/java/naming.xml/PackageCase"/>
<rule ref="rulesets/java/naming.xml/MethodNamingConventions"/>
<rule ref="rulesets/java/naming.xml/MisleadingVariableName"/>
<rule ref="rulesets/java/finalizers.xml/FinalizeShouldBeProtected"/>
<rule ref="rulesets/java/finalizers.xml/FinalizeOnlyCallsSuperFinalize"/>
<rule ref="rulesets/java/naming.xml/BooleanGetMethodName"/>
<rule ref="rulesets/java/naming.xml/GenericsNaming"/>
<rule ref="rulesets/java/unusedcode.xml/UnusedLocalVariable"/>
<rule ref="rulesets/java/unusedcode.xml/UnusedPrivateField"/>
<rule ref="rulesets/java/design.xml/SimplifyConditional"/>
<rule ref="rulesets/java/design.xml/IdempotentOperations"/>
<rule ref="rulesets/java/unusedcode.xml/UnusedPrivateMethod"/>
<rule ref="rulesets/java/design.xml/UnnecessaryLocalBeforeReturn"/>
<rule ref="rulesets/java/unusedcode.xml/UnusedFormalParameter"/>
<rule ref="rulesets/java/design.xml/ConstructorCallsOverridableMethod"/>
<rule ref="rulesets/java/design.xml/InstantiationToGetClass"/>
<rule ref="rulesets/java/design.xml/PreserveStackTrace"/>
<rule ref="rulesets/java/design.xml/CloseResource"/>
<rule ref="rulesets/java/design.xml/EqualsNull"/>
<rule ref="rulesets/java/design.xml/AvoidInstanceofChecksInCatchClause"/>
<rule ref="rulesets/java/design.xml/OptimizableToArrayCall"/>
<rule ref="rulesets/java/design.xml/SimplifyBooleanReturns"/>
<rule ref="rulesets/java/design.xml/MissingStaticMethodInNonInstantiatableClass"/>
<rule ref="rulesets/java/design.xml/CompareObjectsWithEquals"/>
<rule ref="rulesets/java/strictexception.xml/ExceptionAsFlowControl"/>
<rule ref="rulesets/java/optimizations.xml/UseArrayListInsteadOfVector"/>
<rule ref="rulesets/java/android.xml/DoNotHardCodeSDCard"/>
<rule ref="rulesets/java/design.xml/AvoidProtectedFieldInFinalClass"/>
<rule ref="rulesets/java/unusedcode.xml/UnusedModifier"/>
<rule ref="rulesets/java/optimizations.xml/UseArraysAsList"/>
<rule ref="rulesets/java/optimizations.xml/AvoidArrayLoops"/>
<rule ref="rulesets/java/design.xml/FinalFieldCouldBeStatic"/>
<rule ref="rulesets/java/design.xml/SimplifyBooleanExpressions"/>
<rule ref="rulesets/java/android.xml/CallSuperFirst"/>
<rule ref="rulesets/java/android.xml/CallSuperLast"/>
<rule ref="rulesets/java/strictexception.xml/AvoidRethrowingException"/>
<rule ref="rulesets/java/design.xml/NonThreadSafeSingleton"/>
<rule ref="rulesets/java/design.xml/PositionLiteralsFirstInComparisons"/>
<rule ref="rulesets/java/design.xml/SingularField"/>
</ruleset>
Githubから収集したデータを元に、PMDルールファイルを作成しました。
何より、使用しているプロジェクトが少なかったことにびっくりしましたが、
適用ルール、適用除外ルールには、一定の傾向があるような集計結果になりました。
XMLの出力までを自動化もできそうなので、
定期的に結果を収集して、ルールファイルを作成してもいいかもしれません。
以上です。
コメント